Load libraries
library(signal)
library(tidyverse)
library(here)
library(lubridate)
library(dtplyr)
library(sf)
library(knitr)
library(mgcv)
library(future)
library(furrr)
library(progressr)
library(pracma)
library(data.table)
Set a simple console progress bar
handlers("txtprogressbar") # Simple console progress bar
Supress package messages
suppressPackageStartupMessages({
library(mgcv)
library(nlme)
})
Load previously created objects
load(file = "objects/data_RS_Landsat_bands_indices.Rdata")
load(file = "objects/GAM_data_Landsat.Rdata")
#load(file = "objects/smoothed_data_Landsat.Rdata")
load(file = "objects/monthly_avg_indices_Landsat.Rdata")
Load Resurvey db
db_Europa_allobs <- read_csv(
here("data", "clean", "db_Europa_allobs.csv")) %>%
select(PlotObservationID, EUNISa_1, EUNISa_1_descr,
EUNISa_2, EUNISa_2_descr) %>%
mutate(PlotObservationID = factor(PlotObservationID),
EUNISa_1 = factor(EUNISa_1), EUNISa_2 = factor(EUNISa_2))
Define printall function
printall <- function(tibble) {
print(tibble, width = Inf)
}
Read files with band data
I got these files using the GEE code prepared by Bea.
These files contain all observations in the ReSurvey database. In
order to avoid computation problems in GEE, biogeographical units that
contain more than 4500 points have been subdivided in ArcGIS.
# Set the folder path
folder_path <- "C:/Data/MOTIVATE/MOTIVATE_RS_data/Landsat/Bands/all"
# List CSV files
csv_files <- list.files(folder_path, full.names = TRUE, recursive = TRUE)
# Function to extract biogeo and unit from the filename
extract_info <- function(filename) {
first_word <- strsplit(filename, "_")[[1]][1]
biogeo <- str_extract(first_word,
"^(ALP|ANA|ARC|ATL|BLACKSEA|BOR|CON|MACARONESIA|MED|PANONIA|STEPPIC)")
unit <- str_remove(first_word, biogeo)
if (is.na(unit) || unit == "") unit <- NA_character_
list(biogeo = biogeo, unit = unit)
}
# Define column types: force RSrvypl to character, others auto-detected
custom_col_types <- cols(
RSrvypl = col_character(),
RSrvyst = col_character(),
default = col_guess()
)
# Read and process each file
data_list <- lapply(csv_files, function(file) {
info <- extract_info(basename(file)) # Use only the filename
# Read the file
df <- read_csv(file, col_types = custom_col_types) %>%
# Remove columns that give column type problems when combining data
select(-starts_with("EUNIS"), -starts_with("ReSurvey")) %>%
mutate(biogeo = info$biogeo, unit = info$unit)
return(df)
})
# Combine all data
data_RS_Landsat_bands <- bind_rows(data_list) %>%
rename(PlotObservationID = PltObID)
# View the resulting tibble
print(data_RS_Landsat_bands)
# Counts per biogeo and unit
print(data_RS_Landsat_bands %>% count(biogeo, unit), n = 100)
Some checks
Check that the year in the date of the images is not different to the
sampling year:
data_RS_Landsat_bands %>% dplyr::filter(year != year(date))
Check how many different images are for each observation, date and
time:
data_RS_Landsat_bands %>% group_by(PlotObservationID, date, time_utc) %>%
summarise(n_images = n_distinct(image_id), .groups = "drop") %>%
count(n_images)
Average the bands
When there is more than one image for each point and day, average the
values of the bands:
Calculate indices
Save:
Plot n_daytime:
data_RS_Landsat_bands_indices %>%
group_by(PlotObservationID) %>%
summarise(n_days = first(n_days)) %>% ungroup() %>%
ggplot(aes(x = n_days)) + geom_histogram(color = "black", fill = "white") +
theme_minimal()

Compute phenological metrics from models fitted to time series
data
Function
Using GAMs, reweighting and 3 iterations.
Using both a change detection method (maximum slope) and a threshold
method (50% amplitude) to calculate sos and eos.
Approach similar to https://doi.org/10.1016/j.jag.2020.102172 for GAM
fitting and change detection method, and to https://www.mdpi.com/2072-4292/12/22/3738 fot threshold
method.
Define function to compute phenology metrics using GAM fit and NDVI /
EVI / SAVI:
compute_metrics_models <- function(df, index_cols = c("NDVI", "EVI", "SAVI")) {
suppressPackageStartupMessages({
library(mgcv)
library(nlme)
})
plan(multisession) # Set up parallel processing
# Create a list of index-specific data frames
index_dfs <- lapply(index_cols, function(index_col) {
list(index_col = index_col, df = df %>%
select(DOY, PlotObservationID, all_of(index_col)))
})
# Define the processing function for each index
process_index <- function(index_data) {
index_col <- index_data$index_col
df_index <- index_data$df %>%
filter(!is.na(.data[[index_col]])) %>%
arrange(DOY)
plot_id <- unique(df_index$PlotObservationID)
if (nrow(df_index) < 10) {
message(" Skipped: insufficient data (< 10 rows)")
return(tibble(PlotObservationID = plot_id, index = index_col,
sos_slope = NA_real_, sos_threshold = NA_real_,
pos = NA_real_, eos_slope = NA_real_,
eos_threshold = NA_real_, auc_slope = NA_real_,
auc_threshold = NA_real_, Vmax = NA_real_,
DOY = df_index$DOY, value = NA_real_))
}
# Replace early/late DOY values
base_value_early <- mean(df_index %>% filter(DOY <= 50) %>%
pull(index_col), na.rm = TRUE)
base_value_late <- mean(df_index %>% filter(DOY >= 315) %>%
pull(index_col), na.rm = TRUE)
df_index <- df_index %>%
mutate(!!index_col := case_when(
DOY <= 50 ~ base_value_early,
DOY >= 315 ~ base_value_late,
TRUE ~ .data[[index_col]]
))
x <- df_index$DOY
y <- df_index[[index_col]]
weights <- rep(1, length(y))
# GAM fit
pred <- NULL
for (i in 1:3) {
gam_fit <- tryCatch({
mgcv::bam(y ~ s(x, bs = "tp"),weights = weights)
}, error = function(e) {
message(" GAM fitting failed for ", plot_id, " - ", index_col, ": ",
e$message)
return(NULL)
})
if (is.null(gam_fit)) {
return(tibble(
PlotObservationID = plot_id,
index = index_col,
sos_slope = NA_real_,
sos_threshold = NA_real_,
pos = NA_real_,
eos_slope = NA_real_,
eos_threshold = NA_real_,
auc_slope = NA_real_,
auc_threshold = NA_real_,
Vmin_pre = NA_real_,
Vmin_post = NA_real_,
Vmax = NA_real_,
u_sos = NA_real_,
u_eos = NA_real_,
DOY = df_index$DOY,
value = NA_real_))
}
pred <- tryCatch({
predict(gam_fit, newdata = tibble(x = x))
}, error = function(e) {
message("Prediction failed for ", plot_id, " - ", index_col, ": ",
e$message)
return(rep(NA_real_, length(x)))
})
idx_between <- which(x > 50 & x < 315 & !is.na(pred) & pred != 0)
weights <- rep(1, length(y))
weights[idx_between] <- (y[idx_between] / (pred[idx_between] + 1e-6))^4
weights[weights > 1 | is.na(weights)] <- 1
}
# Compute metrics
slope <- c(NA, diff(pred))
idx <- which(x >= 50 & x <= 315)
pos <- if (length(idx) > 0) x[idx][which.max(pred[idx])] else NA_real_
sos_slope <- if (!is.na(pos)) {
idx <- which(x < pos)
if (length(idx) > 0) x[idx][which.max(slope[idx])] else NA_real_
} else NA_real_
eos_slope <- if (!is.na(pos)) {
idx <- which(x > pos)
if (length(idx) > 0) x[idx][which.min(slope[idx])] else NA_real_
} else NA_real_
integration_idx_slope <- which(x >= sos_slope & x <=
eos_slope & !is.na(pred))
auc_slope <- if (length(integration_idx_slope) > 1) {
sum(diff(x[integration_idx_slope]) *
zoo::rollmean(pred[integration_idx_slope], 2))
} else NA_real_
# Vmin antes y después del pico
Vmin_pre <- if (!is.na(pos)) min(pred[x <= pos], na.rm = TRUE)else NA_real_
Vmin_post <- if (!is.na(pos)) min(pred[x >= pos], na.rm = TRUE) else NA_real_
Vmax <- max(pred, na.rm = TRUE)
# Umbrales relativos
p <- 0.5
u_sos <- if (!is.na(Vmin_pre)) Vmin_pre + p * (Vmax - Vmin_pre) else NA_real_
u_eos <- if (!is.na(Vmin_post)) Vmin_post + p * (Vmax - Vmin_post) else NA_real_
# DOY donde se cruzan los umbrales
sos_threshold <- if (!is.na(u_sos) && !is.na(pos)) {
candidates <- x[which(pred >= u_sos & x < pos)]
if (length(candidates) > 0) candidates[1] else NA_real_
} else NA_real_
eos_threshold <- if (!is.na(u_eos) && !is.na(pos)) {
candidates <- x[which(pred >= u_eos & x > pos)]
if (length(candidates) > 0) rev(candidates)[1] else NA_real_
} else NA_real_
integration_idx_threshold <- which(x >= sos_threshold &
x <= eos_threshold & !is.na(pred))
auc_threshold <- if (length(integration_idx_threshold) > 1) {
sum(diff(x[integration_idx_threshold]) *
zoo::rollmean(pred[integration_idx_threshold], 2))
} else NA_real_
# 1. Predicciones por DOY
fits_df <- tibble(
PlotObservationID = unique(df_index$PlotObservationID),
DOY = x,
value = pred,
index = index_col
)
# 2. Métricas resumen
metrics_df <- tibble(
PlotObservationID = unique(df_index$PlotObservationID),
index = index_col,
sos_slope = sos_slope,
sos_threshold = sos_threshold,
pos = pos,
eos_slope = eos_slope,
eos_threshold = eos_threshold,
auc_slope = auc_slope,
auc_threshold = auc_threshold,
Vmin_pre = Vmin_pre,
Vmin_post = Vmin_post,
Vmax = Vmax,
u_sos = u_sos,
u_eos = u_eos
)
# 3. Unir por PlotObservationID, index
final_df <- left_join(fits_df, metrics_df,
by = c("PlotObservationID", "index"))
}
# Run in parallel
results <- future_map(index_dfs, process_index, .progress = TRUE)
results <- purrr::compact(results) # removes NULLs
if (length(results) == 0) return(tibble()) # or return(NULL)
bind_rows(results)
}
Calculation
Apply the function with batch processing.
plan(sequential)
Save
Look:
GAM_data
Save as an object:
Assess time series quality
For the time series to be acceptable, it should have a reasonable
number of time points, and these points should be distributed along
almost all months (could be ok to miss the winter months).
In GAM data, check how many time points are there for each
PlotObservationID, how many months, and which months are missing.
ts_quality <- GAM_data %>%
# Filter only NDVI (all indices will have the same time points)
dplyr::filter(index == "NDVI") %>%
# Get month from DOY
mutate(month = month(ymd("2020-01-01") + days(DOY - 1))) %>%
# For each PlotObservationID
group_by(PlotObservationID) %>%
# Get the number of time points (days) and the number of months
summarise(
n_days = n_distinct(DOY),
n_months = n_distinct(month),
.groups = "drop"
) %>%
left_join(GAM_data %>%
# Filter only NDVI
dplyr::filter(index == "NDVI") %>%
# Get month from DOY
mutate(month = month(ymd("2020-01-01") + days(DOY - 1))) %>%
# Get unique values of PlotObservationID and month
distinct(PlotObservationID, month) %>%
# Add 1 as value
mutate(value = 1) %>%
# Reshape to wide format and add zeros when month is missing
pivot_wider(
names_from = month,
names_prefix = "month",
values_from = value,
values_fill = 0),
by = "PlotObservationID")
Histograms time points and n months:
ggplot(ts_quality, aes(x = n_days)) +
geom_histogram(color = "black", fill = "white") +
xlab("Number of time points (days) in the Landsat time series") +
theme_minimal()

ggplot(ts_quality, aes(x = n_months)) +
geom_histogram(color = "black", fill = "white") +
xlab("Number of months in the Landsat time series") +
theme_minimal()

Count how many PlotObservationIDs have missing data (value 0) for
each month:
obs_missing_month <- ts_quality %>%
summarise(across(starts_with("month"), ~ sum(.x == 0))) %>%
pivot_longer(cols = everything(), names_to = "month", values_to = "nobs_missing")
ggplot(obs_missing_month %>%
mutate(month = factor(month, levels = paste0("month", 1:12))),
aes(x = month, y = nobs_missing)) + geom_bar(stat = "identity") +
ylab("Number of PlotObservationID with missing data") +
ggtitle("Missing data in S2 time series") +
theme_minimal()

Add quality flag:
ts_quality_flag <- ts_quality %>%
rowwise() %>%
mutate(
# If 2 consecutive months of the period March-October are missing
# quality_flag = 0
quality_flag = {
months <- c_across(month3:month10)
if (any(months[-length(months)] == 0 & months[-1] == 0)) 0 else 1
}
) %>%
ungroup()
ts_quality_flag %>% count(quality_flag)
Boxplot comparing moments for different indices
GAM_data %>%
select(PlotObservationID, index, sos_slope, sos_threshold, pos, eos_slope,
eos_threshold) %>% distinct() %>%
pivot_longer(cols = c(sos_slope, sos_threshold, pos, eos_slope, eos_threshold),
names_to = "moment", values_to = "value") %>%
ggplot(aes(x = moment, y = value, fill = index)) + geom_boxplot() +
theme_minimal()

TBD if needed: Plot fit and moments for each PlotObservationID
Quality = 1
# Get unique IDs with quality_flag == 1
ids_q1 <- ts_quality_flag %>%
dplyr::filter(quality_flag == 1) %>%
mutate(PlotObservationID = droplevels(PlotObservationID)) %>%
pull(PlotObservationID)
GAM_data_ids_q1 <- GAM_data %>%
# Join to get biogeo and unit
left_join(data_RS_Landsat_bands_indices %>%
select(PlotObservationID, biogeo, unit) %>%
distinct()) %>%
# Join to get EUNIS info
left_join(db_Europa_allobs %>%
select(PlotObservationID, EUNISa_1, EUNISa_1_descr,
EUNISa_2, EUNISa_2_descr)) %>%
# Join to get original values of indices
left_join(data_RS_Landsat_bands_indices %>%
select(PlotObservationID, DOY, NDVI, EVI, SAVI) %>%
pivot_longer(cols = c(NDVI, EVI, SAVI), names_to = "index",
values_to = "value_orig")) %>%
# Join to get ts_quality data
left_join(ts_quality_flag %>% select(PlotObservationID, quality_flag)) %>%
# Keep only those with quality_flag == 1
dplyr::filter(quality_flag == 1)
Save each plot to a file:
Quality = 0
# Get unique IDs with quality_flag == 0
ids_q0 <- ts_quality_flag %>%
dplyr::filter(quality_flag == 0) %>%
mutate(PlotObservationID = droplevels(PlotObservationID)) %>%
pull(PlotObservationID)
GAM_data_ids_q0 <- GAM_data %>%
# Join to get biogeo and unit
left_join(data_RS_Landsat_bands_indices %>%
select(PlotObservationID, biogeo, unit) %>%
distinct()) %>%
# Join to get EUNIS info
left_join(db_Europa_allobs %>%
select(PlotObservationID, EUNISa_1, EUNISa_1_descr,
EUNISa_2, EUNISa_2_descr)) %>%
# Join to get original values of indices
left_join(data_RS_Landsat_bands_indices %>%
select(PlotObservationID, DOY, NDVI, EVI, SAVI) %>%
pivot_longer(cols = c(NDVI, EVI, SAVI), names_to = "index",
values_to = "value_orig")) %>%
# Join to get ts_quality data
left_join(ts_quality_flag %>%
select(PlotObservationID, n_months, quality_flag)) %>%
# Keep only those with quality_flag == 0
dplyr::filter(quality_flag == 0)
Save each plot to a file:
Smooth the time series of NDMI and NDWI
Function
Using GAM, without replacing values in DOY 1–50 and DOY 315–end with
separate base values, later use only unweighted GAM.
compute_unweighted_fit <- function(
# Data frame df with index values over time (DOY)
df,
# Name of the vegetation indices columns (e.g., "NDVI", "EVI", "SAVI)
index_cols = c("NDMI", "NDWI")
) {
# Initialize list to store results
fits_list <- list()
# Loop over each index column
for (index_col in index_cols) {
df_index <- df %>%
# Remove rows with missing index values and sort data by DOY
filter(!is.na(.data[[index_col]])) %>% arrange(DOY)
# Extract x (DOY) and y (index) vectors for modelling
x <- df_index$DOY
y <- df_index[[index_col]]
# If there are fewer than 11 observations or all values are NA, skip
if (length(x) < 11 || all(is.na(y))) {
next
}
# Fit GAM (unweighted) with a thin plate spline (bs = "tp")
# to smooth the index curve
gam_unweighted <- mgcv::bam(y ~ s(x, bs = "tp"))
pred <- predict(gam_unweighted, newdata = tibble(x = x))
# Create tibble to store original and predicted index values
fits_df <- tibble(
PlotObservationID = unique(df$PlotObservationID),
DOY = x,
index = index_col,
value = pred
)
fits_list[[index_col]] <- fits_df
}
if (length(fits_list) == 0) {
return(tibble())
}
bind_rows(fits_list)
}
Calculation
Apply the function with batch processing.
plan(sequential)
Save
Look:
smoothed_data
Save as an object:
TBD if needed: Plot fit and moments for each PlotObservationID
smoothed_data_ids <- smoothed_data %>%
# Join to get biogeo and unit
left_join(data_RS_Landsat_bands_indices %>%
select(PlotObservationID, biogeo, unit) %>%
distinct()) %>%
# Join to get EUNIS info
left_join(db_Europa_allobs %>%
select(PlotObservationID, EUNISa_1, EUNISa_1_descr,
EUNISa_2, EUNISa_2_descr)) %>%
mutate(PlotObservationID = as.character(PlotObservationID))
Save each plot to a file:
Get indices data (max. and min.)
Careful! These maximum and minimum values are from the smoothed time
series. For NDVI / EVI / SAVI values in DOY 1–50 and DOY 315–end,
remember that the GAM smoothing function replaced the original values
with the mean base value of observations during each of these respective
periods. This was so far not done for NDMI and NDWI.
final_indices_data <- GAM_data %>%
group_by(PlotObservationID, index) %>%
summarise(max = max(value), min = min(value)) %>%
ungroup() %>%
pivot_wider(names_from = index, values_from = c(max, min),
names_glue = "{index}_{.value}") %>%
full_join(
smoothed_data %>%
group_by(PlotObservationID, index) %>%
summarise(max = max(value), min = min(value)) %>%
ungroup() %>%
pivot_wider(names_from = index, values_from = c(max, min),
names_glue = "{index}_{.value}")
)
Get phenology data
Use GAM iter_3 to get dates of the moments, values at those moments
and AUC (time-integrated indices) between SOS and EOS:
# Join to get values at SOS, POS, EOS and auc
final_phenology_data <- GAM_data %>%
mutate(
stage = case_when(
DOY == sos_slope ~ "sos_slope",
DOY == sos_threshold ~ "sos_threshold",
DOY == pos ~ "pos",
DOY == eos_slope ~ "eos_slope",
DOY == eos_threshold ~ "eos_threshold",
TRUE ~ NA_character_
)
) %>%
dplyr::filter(!is.na(stage)) %>%
select(PlotObservationID, index, stage, doy = DOY, value) %>%
pivot_wider(
names_from = c(index, stage),
values_from = c(doy, value),
names_glue = "{index}_{stage}_{.value}"
) %>%
# Convert list cols to regular numeric cols
mutate(
NDVI_sos_slope_value = map_dbl(NDVI_sos_slope_value, 1),
NDVI_sos_threshold_value = map_dbl(NDVI_sos_threshold_value, 1),
NDVI_pos_value = map_dbl(NDVI_pos_value, 1),
NDVI_eos_slope_value = map_dbl(NDVI_eos_slope_value, 1),
NDVI_eos_threshold_value = map_dbl(NDVI_eos_threshold_value, 1),
EVI_sos_slope_value = map_dbl(EVI_sos_slope_value, 1),
EVI_sos_threshold_value = map_dbl(EVI_sos_threshold_value, 1),
EVI_pos_value = map_dbl(EVI_pos_value, 1),
EVI_eos_slope_value = map_dbl(EVI_eos_slope_value, 1),
EVI_eos_threshold_value = map_dbl(EVI_eos_threshold_value, 1),
SAVI_sos_slope_value = map_dbl(SAVI_sos_slope_value, 1),
SAVI_sos_threshold_value = map_dbl(SAVI_sos_threshold_value, 1),
SAVI_pos_value = map_dbl(SAVI_pos_value, 1),
SAVI_eos_slope_value = map_dbl(SAVI_eos_slope_value, 1),
SAVI_eos_threshold_value = map_dbl(SAVI_eos_threshold_value, 1)
) %>%
full_join(GAM_data %>%
distinct(PlotObservationID, index, auc_slope, auc_threshold) %>%
pivot_wider(names_from = index, values_from = c(auc_slope, auc_threshold),
names_glue = "{index}_{.value}"))
Join indices and phenology data
final_RS_data <- full_join(
# Indices data (max and min)
final_indices_data,
# Average values of indices per month
monthly_avg_indices %>%
pivot_wider(names_from = index, values_from = c(avg_value_01:avg_value_12),
names_glue = "{index}_{.value}")
) %>%
full_join(
# Phenology data
final_phenology_data
) %>%
# Sort cols in alphabetical order
select(PlotObservationID, sort(names(.)[names(.) != "PlotObservationID"]))
Add EUNIS codes
final_RS_data <- final_RS_data %>% left_join(db_Europa_allobs)
data_RS_Landsat_bands_indices <- data_RS_Landsat_bands_indices %>%
left_join(db_Europa_allobs)
Monthly spectrophenology per habitat type
# Prepare the data
data_monthly_EUNISa_1 <- data_RS_Landsat_bands_indices %>%
mutate(month = lubridate::month(date, label = TRUE, abbr = TRUE,
locale="EN-us")) %>%
group_by(month, EUNISa_1, EUNISa_1_descr) %>%
summarise(
mean_NDVI = mean(NDVI, na.rm = TRUE),
sd_NDVI = sd(NDVI, na.rm = TRUE),
n_NDVI = sum(!is.na(NDVI)),
mean_EVI = mean(EVI, na.rm = TRUE),
sd_EVI = sd(EVI, na.rm = TRUE),
n_EVI = sum(!is.na(EVI)),
mean_SAVI = mean(SAVI, na.rm = TRUE),
sd_SAVI = sd(SAVI, na.rm = TRUE),
n_SAVI = sum(!is.na(SAVI)),
.groups = "drop"
)
data_monthly_EUNISa_1 <- data_monthly_EUNISa_1 %>%
# Add label with n
left_join(data_monthly_EUNISa_1 %>%
group_by(EUNISa_1) %>%
summarise(n_total = sum(n_NDVI, na.rm = TRUE)),
by = "EUNISa_1") %>%
mutate(EUNISa_1_label = paste0(EUNISa_1, " (n = ", n_total, ")"))
data_monthly_EUNISa_2 <- data_RS_Landsat_bands_indices %>%
mutate(month = lubridate::month(date, label = TRUE, abbr = TRUE,
locale="EN-us")) %>%
group_by(month, EUNISa_1, EUNISa_1_descr, EUNISa_2, EUNISa_2_descr) %>%
summarise(
mean_NDVI = mean(NDVI, na.rm = TRUE),
sd_NDVI = sd(NDVI, na.rm = TRUE),
n_NDVI = sum(!is.na(NDVI)),
mean_EVI = mean(EVI, na.rm = TRUE),
sd_EVI = sd(EVI, na.rm = TRUE),
n_EVI = sum(!is.na(EVI)),
mean_SAVI = mean(SAVI, na.rm = TRUE),
sd_SAVI = sd(SAVI, na.rm = TRUE),
n_SAVI = sum(!is.na(SAVI)),
.groups = "drop"
)
data_monthly_EUNISa_2 <- data_monthly_EUNISa_2 %>%
# Add label with n
left_join(data_monthly_EUNISa_2 %>%
group_by(EUNISa_2) %>%
summarise(n_total = sum(n_NDVI, na.rm = TRUE)),
by = "EUNISa_2") %>%
mutate(EUNISa_2_label = paste0(EUNISa_2, " (n = ", n_total, ")"))
EUNIS level 1
# Plots
# EUNISa_1
ggplot(data_monthly_EUNISa_1 %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_1_label, EUNISa_1_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_1, EUNISa_1_descr, sep = " - ")),
aes(x = month, y = mean_NDVI, color = EUNIS,
group = EUNISa_1_label)) +
geom_point() +
geom_line(aes(group = EUNISa_1)) +
#geom(aes(ymin = mean_NDVI - sd_NDVI, ymax = mean_NDVI + sd_NDVI),
#width = 0.2) +
labs(
title = "Monthly NDVI by Habitat Type",
x = "Month",
y = "NDVI",
color = "Habitat (EUNIS1)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS1_NDVI.jpeg"),
dpi = 300, width = 6, height = 2.5)

ggplot(data_monthly_EUNISa_1 %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_1_label, EUNISa_1_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_1, EUNISa_1_descr, sep = " - ")),
aes(x = month, y = mean_EVI, color = EUNIS,
group = EUNISa_1_label)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_EVI - sd_EVI, ymax = mean_EVI + sd_EVI),
#width = 0.2) +
labs(
title = "Monthly EVI by Habitat Type",
x = "Month",
y = "EVI",
color = "Habitat (EUNIS1)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS1_EVI.jpeg"),
dpi = 300, width = 6, height = 2.5)

ggplot(data_monthly_EUNISa_1 %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_1_label, EUNISa_1_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_1, EUNISa_1_descr, sep = " - ")),
aes(x = month, y = mean_SAVI, color = EUNIS,
group = EUNISa_1_label)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_SAVI - sd_SAVI, ymax = mean_SAVI + sd_SAVI),
#width = 0.2) +
labs(
title = "Monthly SAVI by Habitat Type",
x = "Month",
y = "SAVI",
color = "Habitat (EUNIS1)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS1_SAVI.jpeg"),
dpi = 300, width = 6, height = 2.5)

EUNIS level 2
Q
# NDVI
ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "Q" & !is.na(EUNISa_2)) %>%
# Remove those with EUNIS level 2 that does not match current classif
dplyr::filter(EUNISa_2 != "Qa" & EUNISa_2 != "Qb") %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_NDVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_NDVI - sd_NDVI, ymax = mean_NDVI + sd_NDVI),
#width = 0.2) +
labs(
title = "Monthly NDVI by Habitat Type",
x = "Month",
y = "NDVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_Q_NDVI.jpeg"),
dpi = 300, width = 7, height = 2.5)

# EVI
ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "Q" & !is.na(EUNISa_2)) %>%
# Remove those with EUNIS level 2 that does not match current classif
dplyr::filter(EUNISa_2 != "Qa" & EUNISa_2 != "Qb") %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_EVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_EVI - sd_EVI, ymax = mean_EVI + sd_EVI),
#width = 0.2) +
labs(
title = "Monthly EVI by Habitat Type",
x = "Month",
y = "EVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_Q_EVI.jpeg"),
dpi = 300, width = 7, height = 2.5)

# SAVI
ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "Q" & !is.na(EUNISa_2)) %>%
# Remove those with EUNIS level 2 that does not match current classif
dplyr::filter(EUNISa_2 != "Qa" & EUNISa_2 != "Qb") %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_SAVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_SAVI - sd_SAVI, ymax = mean_SAVI + sd_SAVI),
#width = 0.2) +
labs(
title = "Monthly SAVI by Habitat Type",
x = "Month",
y = "SAVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_Q_SAVI.jpeg"),
dpi = 300, width = 7, height = 2.5)

R
ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "R" & !is.na(EUNISa_2)) %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_NDVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_NDVI - sd_NDVI, ymax = mean_NDVI + sd_NDVI),
#width = 0.2) +
labs(
title = "Monthly NDVI by Habitat Type",
x = "Month",
y = "NDVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_R_NDVI.jpeg"),
dpi = 300, width = 7, height = 2.5)

ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "R" & !is.na(EUNISa_2)) %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_EVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_EVI - sd_EVI, ymax = mean_EVI + sd_EVI),
#width = 0.2) +
labs(
title = "Monthly EVI by Habitat Type",
x = "Month",
y = "EVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_R_EVI.jpeg"),
dpi = 300, width = 7, height = 2.5)

ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "R" & !is.na(EUNISa_2)) %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_SAVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_SAVI - sd_SAVI, ymax = mean_SAVI + sd_SAVI),
#width = 0.2) +
labs(
title = "Monthly SAVI by Habitat Type",
x = "Month",
y = "SAVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_R_SAVI.jpeg"),
dpi = 300, width = 7, height = 2.5)

S
ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "S" & !is.na(EUNISa_2)) %>%
# Remove those with EUNIS level 2 that does not match current classif
dplyr::filter(EUNISa_2 != "Sa" & EUNISa_2 != "Sb") %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_NDVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_NDVI - sd_NDVI, ymax = mean_NDVI + sd_NDVI),
#width = 0.2) +
labs(
title = "Monthly NDVI by Habitat Type",
x = "Month",
y = "NDVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_S_NDVI.jpeg"),
dpi = 300, width = 8, height = 2.5)

ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "S" & !is.na(EUNISa_2)) %>%
# Remove those with EUNIS level 2 that does not match current classif
dplyr::filter(EUNISa_2 != "Sa" & EUNISa_2 != "Sb") %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_EVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_EVI - sd_EVI, ymax = mean_EVI + sd_EVI),
#width = 0.2) +
labs(
title = "Monthly EVI by Habitat Type",
x = "Month",
y = "EVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_S_EVI.jpeg"),
dpi = 300, width = 8, height = 2.5)

ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "S" & !is.na(EUNISa_2)) %>%
# Remove those with EUNIS level 2 that does not match current classif
dplyr::filter(EUNISa_2 != "Sa" & EUNISa_2 != "Sb") %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_SAVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_SAVI - sd_SAVI, ymax = mean_SAVI + sd_SAVI),
#width = 0.2) +
labs(
title = "Monthly SAVI by Habitat Type",
x = "Month",
y = "SAVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_S_SAVI.jpeg"),
dpi = 300, width = 8, height = 2.5)

T
ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "T" & !is.na(EUNISa_2)) %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_NDVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_NDVI - sd_NDVI, ymax = mean_NDVI + sd_NDVI),
#width = 0.2) +
labs(
title = "Monthly NDVI by Habitat Type",
x = "Month",
y = "NDVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_T_NDVI.jpeg"),
dpi = 300, width = 6, height = 2.5)

ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "T" & !is.na(EUNISa_2)) %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_EVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_EVI - sd_EVI, ymax = mean_EVI + sd_EVI),
#width = 0.2) +
labs(
title = "Monthly EVI by Habitat Type",
x = "Month",
y = "EVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_T_EVI.jpeg"),
dpi = 300, width = 6, height = 2.5)

ggplot(data_monthly_EUNISa_2 %>%
dplyr::filter(EUNISa_1 == "T" & !is.na(EUNISa_2)) %>%
# If we want to have n points
# mutate(EUNIS = paste(EUNISa_2_label, EUNISa_2_descr, sep = " - ")),
mutate(EUNIS = paste(EUNISa_2, EUNISa_2_descr, sep = " - ")),
aes(x = month, y = mean_SAVI, color = EUNIS, group = EUNIS)) +
geom_point() +
geom_line() +
#geom(aes(ymin = mean_SAVI - sd_SAVI, ymax = mean_SAVI + sd_SAVI),
#width = 0.2) +
labs(
title = "Monthly SAVI by Habitat Type",
x = "Month",
y = "SAVI",
color = "Habitat (EUNIS2)"
) +
theme_minimal()
ggsave(
here("output", "figures", "monthly_spectrophenology_Landsat", "EUNIS2_T_SAVI.jpeg"),
dpi = 300, width = 6, height = 2.5)

Calculate other phenological metrics
final_RS_data <- final_RS_data %>%
mutate(
# with slope method
# Growing season duration
NDVI_slope_gsd = NDVI_eos_slope_doy - NDVI_sos_slope_doy,
EVI_slope_gsd = NDVI_eos_slope_doy - NDVI_sos_slope_doy,
SAVI_slope_gsd = SAVI_eos_slope_doy - SAVI_sos_slope_doy,
# Difference in value between pos and sos
NDVI_diff_pos_sos_slope_value = NDVI_pos_value - NDVI_sos_slope_value,
EVI_diff_pos_sos_slope_value = EVI_pos_value - EVI_sos_slope_value,
SAVI_diff_pos_sos_slope_value = SAVI_pos_value - SAVI_sos_slope_value,
# Difference in value between pos and eos
NDVI_diff_pos_eos_slope_value = NDVI_pos_value - NDVI_eos_slope_value,
EVI_diff_pos_eos_slope_value = EVI_pos_value - EVI_eos_slope_value,
SAVI_diff_pos_eos_slope_value = SAVI_pos_value - SAVI_eos_slope_value,
# Difference in doy between pos and sos
NDVI_diff_pos_sos_slope_doy = NDVI_pos_doy - NDVI_sos_slope_doy,
EVI_diff_pos_sos_slope_doy = EVI_pos_doy - EVI_sos_slope_doy,
SAVI_diff_pos_sos_slope_doy = SAVI_pos_doy - SAVI_sos_slope_doy,
# Absolute difference in doy between pos and eos
NDVI_diff_pos_eos_slope_doy = abs(NDVI_pos_doy - NDVI_eos_slope_doy),
EVI_diff_pos_eos_slope_doy = abs(EVI_pos_doy - EVI_eos_slope_doy),
SAVI_diff_pos_eos_slope_doy = abs(SAVI_pos_doy - SAVI_eos_slope_doy),
# With threshold method
# Growing season duration
NDVI_threshold_gsd = NDVI_eos_threshold_doy - NDVI_sos_threshold_doy,
EVI_threshold_gsd = NDVI_eos_threshold_doy - NDVI_sos_threshold_doy,
SAVI_threshold_gsd = SAVI_eos_threshold_doy - SAVI_sos_threshold_doy,
# Difference in value between pos and sos
NDVI_diff_pos_sos_threshold_value =
NDVI_pos_value - NDVI_sos_threshold_value,
EVI_diff_pos_sos_threshold_value =
EVI_pos_value - EVI_sos_threshold_value,
SAVI_diff_pos_sos_threshold_value =
SAVI_pos_value - SAVI_sos_threshold_value,
# Difference in value between pos and eos
NDVI_diff_pos_eos_threshold_value =
NDVI_pos_value - NDVI_eos_threshold_value,
EVI_diff_pos_eos_threshold_value =
EVI_pos_value - EVI_eos_threshold_value,
SAVI_diff_pos_eos_threshold_value =
SAVI_pos_value - SAVI_eos_threshold_value,
# Difference in doy between pos and sos
NDVI_diff_pos_sos_threshold_doy = NDVI_pos_doy - NDVI_sos_threshold_doy,
EVI_diff_pos_sos_threshold_doy = EVI_pos_doy - EVI_sos_threshold_doy,
SAVI_diff_pos_sos_threshold_doy = SAVI_pos_doy - SAVI_sos_threshold_doy,
# Absolute difference in doy between pos and eos
NDVI_diff_pos_eos_threshold_doy =
abs(NDVI_pos_doy - NDVI_eos_threshold_doy),
EVI_diff_pos_eos_threshold_doy =
abs(EVI_pos_doy - EVI_eos_threshold_doy),
SAVI_diff_pos_eos_threshold_doy =
abs(SAVI_pos_doy - SAVI_eos_threshold_doy),
# With months method
# Difference in value between pos and march
NDVI_diff_pos_march_value = NDVI_pos_value - NDVI_avg_value_03,
EVI_diff_pos_march_value = EVI_pos_value - EVI_avg_value_03,
SAVI_diff_pos_march_value = SAVI_pos_value - SAVI_avg_value_03,
# Difference in value between pos and oct
NDVI_diff_pos_oct_value = NDVI_pos_value - NDVI_avg_value_10,
EVI_diff_pos_oct_value = EVI_pos_value - EVI_avg_value_10,
SAVI_diff_pos_oct_value = SAVI_pos_value - SAVI_avg_value_10,
# Difference in doy between pos and march
NDVI_diff_pos_march_doy = NDVI_pos_doy - 75,
EVI_diff_pos_march_doy = EVI_pos_doy - 75,
SAVI_diff_pos_march_doy = SAVI_pos_doy - 75,
# Difference in doy between pos and oct
NDVI_diff_pos_oct_doy = abs(NDVI_pos_doy - 285),
EVI_diff_pos_oct_doy = abs(EVI_pos_doy - 285),
SAVI_diff_pos_oct_doy = abs(SAVI_pos_doy - 285)
)
Checks
Slope method
# Growing season duration should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_slope_gsd <= 0 | EVI_slope_gsd <= 0 |
SAVI_slope_gsd <= 0))
[1] 0
# Difference in value between pos and sos should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_diff_pos_sos_slope_value <= 0))
[1] 133
nrow(final_RS_data %>%
dplyr::filter(EVI_diff_pos_sos_slope_value <= 0))
[1] 301
nrow(final_RS_data %>%
dplyr::filter(SAVI_diff_pos_sos_slope_value <= 0))
[1] 128
# Difference in value between pos and eos should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_diff_pos_eos_slope_value <= 0))
[1] 646
nrow(final_RS_data %>%
dplyr::filter(EVI_diff_pos_eos_slope_value <= 0))
[1] 1749
nrow(final_RS_data %>%
dplyr::filter(SAVI_diff_pos_eos_slope_value <= 0))
[1] 632
# Difference in doy between pos and sos should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_diff_pos_sos_slope_doy <= 0 |
EVI_diff_pos_sos_slope_doy <= 0 |
SAVI_diff_pos_sos_slope_doy <= 0))
[1] 0
# Difference in doy between eos and pos should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_diff_pos_eos_slope_doy <= 0 |
EVI_diff_pos_eos_slope_doy <= 0 |
SAVI_diff_pos_eos_slope_doy <= 0))
[1] 0
Threshold method
# Growing season duration should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_threshold_gsd <= 0 | EVI_threshold_gsd <= 0 |
SAVI_threshold_gsd <= 0))
[1] 0
# Difference in value between pos and sos should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_diff_pos_sos_threshold_value <= 0))
[1] 274
nrow(final_RS_data %>%
dplyr::filter(EVI_diff_pos_sos_threshold_value <= 0))
[1] 557
nrow(final_RS_data %>%
dplyr::filter(SAVI_diff_pos_sos_threshold_value <= 0))
[1] 239
# Difference in value between pos and eos should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_diff_pos_eos_threshold_value <= 0))
[1] 224
nrow(final_RS_data %>%
dplyr::filter(EVI_diff_pos_eos_threshold_value <= 0))
[1] 449
nrow(final_RS_data %>%
dplyr::filter(SAVI_diff_pos_eos_threshold_value <= 0))
[1] 204
# Difference in doy between pos and sos should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_diff_pos_sos_threshold_doy <= 0 |
EVI_diff_pos_sos_threshold_doy <= 0 |
SAVI_diff_pos_sos_threshold_doy <= 0))
[1] 0
# Difference in doy between eos and pos should be positive
nrow(final_RS_data %>%
dplyr::filter(NDVI_diff_pos_eos_threshold_doy <= 0 |
EVI_diff_pos_eos_threshold_doy <= 0 |
SAVI_diff_pos_eos_threshold_doy <= 0))
[1] 0
Maybe TBD: Peak detection
Preliminary code in 9_S2_bands_all_extract_data. So far not working,
see if we finally do this or not.
Add some columns needed
final_RS_data <- final_RS_data %>%
left_join(
data_RS_Landsat_bands_indices %>%
distinct(PlotObservationID, year, biogeo, unit, Lctnmth)
)
Add canopy height data
Read the data:
data_RS_CH <- read_csv(
"C:/Data/MOTIVATE/MOTIVATE_RS_data/Canopy_Height_1m/Europe_points_CanopyHeight_1m.csv")
db_Europa <- read_csv(
here("..", "DB_first_check", "data", "clean","db_Europa_20250107.csv")
)
data_RS_CH_ID <- db_Europa %>%
select(PlotObservationID, obs_unique_id) %>%
right_join(data_RS_CH %>%
# Rename to be able to join on this column
rename(obs_unique_id = obs_unique)) %>%
select(PlotObservationID, canopy_height)
Join:
final_RS_data <- final_RS_data %>%
left_join(data_RS_CH_ID %>%
mutate(PlotObservationID = factor(PlotObservationID)))
Save to clean data
write_tsv(final_RS_data,
here("data", "clean","final_RS_data_bands_Landsat_all_20250925.csv"))
Session info
sessionInfo()
LS0tDQp0aXRsZTogIlNjcmlwdCB0byB3b3JrIHdpdGggUzIgYmFuZHMgZGVyaXZlZCBmcm9tIEdFRSINCnN1YnRpdGxlOiAiUmVhZCBhbmQgbWFuaXB1bGF0aW9uIGRhdGEsIGNhbGN1bGF0ZSBpbmRpY2VzIg0KYXV0aG9yOiAiQWxpY2lhIFZhbGTDqXMiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiAlWScpYCINCm91dHB1dDoNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KIyBMb2FkIGxpYnJhcmllcw0KDQpgYGB7cn0NCmxpYnJhcnkoc2lnbmFsKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZHRwbHlyKQ0KbGlicmFyeShzZikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KG1nY3YpDQpsaWJyYXJ5KGZ1dHVyZSkNCmxpYnJhcnkoZnVycnIpDQpsaWJyYXJ5KHByb2dyZXNzcikNCmxpYnJhcnkocHJhY21hKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KYGBgDQoNCiMgU2V0IGEgc2ltcGxlIGNvbnNvbGUgcHJvZ3Jlc3MgYmFyDQoNCmBgYHtyfQ0KaGFuZGxlcnMoInR4dHByb2dyZXNzYmFyIikgIyBTaW1wbGUgY29uc29sZSBwcm9ncmVzcyBiYXINCmBgYA0KDQojIFN1cHJlc3MgcGFja2FnZSBtZXNzYWdlcw0KDQpgYGB7cn0NCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7DQogIGxpYnJhcnkobWdjdikNCiAgbGlicmFyeShubG1lKQ0KfSkNCmBgYA0KDQojIExvYWQgcHJldmlvdXNseSBjcmVhdGVkIG9iamVjdHMNCg0KYGBge3J9DQpsb2FkKGZpbGUgPSAib2JqZWN0cy9kYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcy5SZGF0YSIpDQpsb2FkKGZpbGUgPSAib2JqZWN0cy9HQU1fZGF0YV9MYW5kc2F0LlJkYXRhIikNCmxvYWQoZmlsZSA9ICJvYmplY3RzL3Ntb290aGVkX2RhdGFfTGFuZHNhdC5SZGF0YSIpDQpsb2FkKGZpbGUgPSAib2JqZWN0cy9tb250aGx5X2F2Z19pbmRpY2VzX0xhbmRzYXQuUmRhdGEiKQ0KYGBgDQoNCiMgTG9hZCBSZXN1cnZleSBkYg0KDQpgYGB7cn0NCmRiX0V1cm9wYV9hbGxvYnMgPC0gcmVhZF9jc3YoDQogIGhlcmUoImRhdGEiLCAiY2xlYW4iLCAiZGJfRXVyb3BhX2FsbG9icy5jc3YiKSkgJT4lDQogIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgRVVOSVNhXzEsIEVVTklTYV8xX2Rlc2NyLA0KICAgICAgICAgRVVOSVNhXzIsIEVVTklTYV8yX2Rlc2NyKSAlPiUNCiAgbXV0YXRlKFBsb3RPYnNlcnZhdGlvbklEID0gZmFjdG9yKFBsb3RPYnNlcnZhdGlvbklEKSwNCiAgICAgICAgIEVVTklTYV8xID0gZmFjdG9yKEVVTklTYV8xKSwgRVVOSVNhXzIgPSBmYWN0b3IoRVVOSVNhXzIpKQ0KYGBgDQoNCiMgRGVmaW5lIHByaW50YWxsIGZ1bmN0aW9uDQoNCmBgYHtyfQ0KcHJpbnRhbGwgPC0gZnVuY3Rpb24odGliYmxlKSB7DQogIHByaW50KHRpYmJsZSwgd2lkdGggPSBJbmYpDQogIH0NCmBgYA0KDQojIFJlYWQgZmlsZXMgd2l0aCBiYW5kIGRhdGENCg0KSSBnb3QgdGhlc2UgZmlsZXMgdXNpbmcgdGhlIEdFRSBjb2RlIHByZXBhcmVkIGJ5IEJlYS4NCg0KVGhlc2UgZmlsZXMgY29udGFpbiBhbGwgb2JzZXJ2YXRpb25zIGluIHRoZSBSZVN1cnZleSBkYXRhYmFzZS4gSW4gb3JkZXIgdG8gYXZvaWQgY29tcHV0YXRpb24gcHJvYmxlbXMgaW4gR0VFLCBiaW9nZW9ncmFwaGljYWwgdW5pdHMgdGhhdCBjb250YWluIG1vcmUgdGhhbiA0NTAwIHBvaW50cyBoYXZlIGJlZW4gc3ViZGl2aWRlZCBpbiBBcmNHSVMuDQoNCmBgYHtyfQ0KIyBTZXQgdGhlIGZvbGRlciBwYXRoDQpmb2xkZXJfcGF0aCA8LSAiQzovRGF0YS9NT1RJVkFURS9NT1RJVkFURV9SU19kYXRhL0xhbmRzYXQvQmFuZHMvYWxsIg0KDQojIExpc3QgQ1NWIGZpbGVzDQpjc3ZfZmlsZXMgPC0gbGlzdC5maWxlcyhmb2xkZXJfcGF0aCwgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpDQoNCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBiaW9nZW8gYW5kIHVuaXQgZnJvbSB0aGUgZmlsZW5hbWUNCmV4dHJhY3RfaW5mbyA8LSBmdW5jdGlvbihmaWxlbmFtZSkgew0KICBmaXJzdF93b3JkIDwtIHN0cnNwbGl0KGZpbGVuYW1lLCAiXyIpW1sxXV1bMV0NCiAgYmlvZ2VvIDwtIHN0cl9leHRyYWN0KGZpcnN0X3dvcmQsDQogICAgICAgICAgICAgICAgICAgICAgICAiXihBTFB8QU5BfEFSQ3xBVEx8QkxBQ0tTRUF8Qk9SfENPTnxNQUNBUk9ORVNJQXxNRUR8UEFOT05JQXxTVEVQUElDKSIpDQogIHVuaXQgPC0gc3RyX3JlbW92ZShmaXJzdF93b3JkLCBiaW9nZW8pDQogIGlmIChpcy5uYSh1bml0KSB8fCB1bml0ID09ICIiKSB1bml0IDwtIE5BX2NoYXJhY3Rlcl8NCiAgbGlzdChiaW9nZW8gPSBiaW9nZW8sIHVuaXQgPSB1bml0KQ0KICB9DQoNCg0KIyBEZWZpbmUgY29sdW1uIHR5cGVzOiBmb3JjZSBSU3J2eXBsIHRvIGNoYXJhY3Rlciwgb3RoZXJzIGF1dG8tZGV0ZWN0ZWQNCmN1c3RvbV9jb2xfdHlwZXMgPC0gY29scygNCiAgUlNydnlwbCA9IGNvbF9jaGFyYWN0ZXIoKSwNCiAgUlNydnlzdCA9IGNvbF9jaGFyYWN0ZXIoKSwNCiAgZGVmYXVsdCA9IGNvbF9ndWVzcygpDQopDQoNCiMgUmVhZCBhbmQgcHJvY2VzcyBlYWNoIGZpbGUNCmRhdGFfbGlzdCA8LSBsYXBwbHkoY3N2X2ZpbGVzLCBmdW5jdGlvbihmaWxlKSB7DQogIGluZm8gPC0gZXh0cmFjdF9pbmZvKGJhc2VuYW1lKGZpbGUpKSAjIFVzZSBvbmx5IHRoZSBmaWxlbmFtZQ0KICANCiAgIyBSZWFkIHRoZSBmaWxlDQogIGRmIDwtIHJlYWRfY3N2KGZpbGUsIGNvbF90eXBlcyA9IGN1c3RvbV9jb2xfdHlwZXMpICU+JQ0KICAgICMgUmVtb3ZlIGNvbHVtbnMgdGhhdCBnaXZlIGNvbHVtbiB0eXBlIHByb2JsZW1zIHdoZW4gY29tYmluaW5nIGRhdGENCiAgICBzZWxlY3QoLXN0YXJ0c193aXRoKCJFVU5JUyIpLCAtc3RhcnRzX3dpdGgoIlJlU3VydmV5IikpICU+JQ0KICAgIG11dGF0ZShiaW9nZW8gPSBpbmZvJGJpb2dlbywgdW5pdCA9IGluZm8kdW5pdCkNCiAgDQogIHJldHVybihkZikNCiAgfSkNCg0KIyBDb21iaW5lIGFsbCBkYXRhDQpkYXRhX1JTX0xhbmRzYXRfYmFuZHMgPC0gYmluZF9yb3dzKGRhdGFfbGlzdCkgJT4lDQogIHJlbmFtZShQbG90T2JzZXJ2YXRpb25JRCA9IFBsdE9iSUQpDQoNCiMgVmlldyB0aGUgcmVzdWx0aW5nIHRpYmJsZQ0KcHJpbnQoZGF0YV9SU19MYW5kc2F0X2JhbmRzKQ0KDQojIENvdW50cyBwZXIgYmlvZ2VvIGFuZCB1bml0DQpwcmludChkYXRhX1JTX0xhbmRzYXRfYmFuZHMgJT4lIGNvdW50KGJpb2dlbywgdW5pdCksIG4gPSAxMDApDQpgYGANCg0KIyBTb21lIGNoZWNrcw0KDQpDaGVjayB0aGF0IHRoZSB5ZWFyIGluIHRoZSBkYXRlIG9mIHRoZSBpbWFnZXMgaXMgbm90IGRpZmZlcmVudCB0byB0aGUgc2FtcGxpbmcgeWVhcjoNCg0KYGBge3J9DQpkYXRhX1JTX0xhbmRzYXRfYmFuZHMgJT4lIGRwbHlyOjpmaWx0ZXIoeWVhciAhPSB5ZWFyKGRhdGUpKQ0KYGBgDQoNCkNoZWNrIGhvdyBtYW55IGRpZmZlcmVudCBpbWFnZXMgYXJlIGZvciBlYWNoIG9ic2VydmF0aW9uLCBkYXRlIGFuZCB0aW1lOg0KDQpgYGB7cn0NCmRhdGFfUlNfTGFuZHNhdF9iYW5kcyAlPiUgZ3JvdXBfYnkoUGxvdE9ic2VydmF0aW9uSUQsIGRhdGUsIHRpbWVfdXRjKSAlPiUNCiAgc3VtbWFyaXNlKG5faW1hZ2VzID0gbl9kaXN0aW5jdChpbWFnZV9pZCksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBjb3VudChuX2ltYWdlcykNCmBgYA0KDQojIEF2ZXJhZ2UgdGhlIGJhbmRzDQoNCldoZW4gdGhlcmUgaXMgbW9yZSB0aGFuIG9uZSBpbWFnZSBmb3IgZWFjaCBwb2ludCBhbmQgZGF5LCBhdmVyYWdlIHRoZSB2YWx1ZXMgb2YgdGhlIGJhbmRzOg0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBTdW1tYXJpemUgdGhlIGJhbmQgdmFsdWVzIGNvbmRpdGlvbmFsbHkNCmJhbmRfc3VtbWFyeSA8LSBkYXRhX1JTX0xhbmRzYXRfYmFuZHMgJT4lDQogIGdyb3VwX2J5KFBsb3RPYnNlcnZhdGlvbklELCBkYXRlKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG5faW1hZ2VzID0gbl9kaXN0aW5jdChpbWFnZV9pZCksDQogICAgQmx1ZSA9IGlmIChuX2ltYWdlcyA+IDEpIG1lYW4oQmx1ZSwgbmEucm0gPSBUUlVFKSBlbHNlIGZpcnN0KEJsdWUpLA0KICAgIEdyZWVuICA9IGlmIChuX2ltYWdlcyA+IDEpIG1lYW4oR3JlZW4sICBuYS5ybSA9IFRSVUUpIGVsc2UgZmlyc3QoR3JlZW4pLA0KICAgIE5JUiAgPSBpZiAobl9pbWFnZXMgPiAxKSBtZWFuKE5JUiwgIG5hLnJtID0gVFJVRSkgZWxzZSBmaXJzdChOSVIpLA0KICAgIFJlZCAgPSBpZiAobl9pbWFnZXMgPiAxKSBtZWFuKFJlZCwgIG5hLnJtID0gVFJVRSkgZWxzZSBmaXJzdChSZWQpLA0KICAgIFNXSVIxID0gaWYgKG5faW1hZ2VzID4gMSkgbWVhbihTV0lSMSwgIG5hLnJtID0gVFJVRSkgZWxzZSBmaXJzdChTV0lSMSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApIA0KDQojIENhbGN1bGF0ZSBob3cgbWFueSBkaWZmZXJlbnQgZGF5cyBmb3IgZWFjaCBQbG90T2JzZXJ2YXRpb25JRA0Kbl9kYXlzIDwtIGJhbmRfc3VtbWFyeSAlPiUNCiAgZ3JvdXBfYnkoUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICBzdW1tYXJpc2Uobl9kYXlzID0gbl9kaXN0aW5jdChkYXRlKSkNCg0KIyBKb2luIGJhY2sgdG8gb3JpZ2luYWwgZGF0YQ0KZGF0YV9SU19MYW5kc2F0X2JhbmRzX3VwZGF0ZWQgPC0gZGF0YV9SU19MYW5kc2F0X2JhbmRzICU+JQ0KICAjIFJlbW92ZSBvbGQgYmFuZCB2YWx1ZXMNCiAgc2VsZWN0KC1CbHVlLCAtR3JlZW4sIC1OSVIsIC1SZWQsIC1TV0lSMSkgJT4lDQogICMgSm9pbiBiYW5kX3N1bW1hcnkNCiAgbGVmdF9qb2luKGJhbmRfc3VtbWFyeSwgYnkgPSBjKCJQbG90T2JzZXJ2YXRpb25JRCIsICJkYXRlIikpICU+JQ0KICAjIEtlZXAgb25lIHJvdyBwZXIgZ3JvdXANCiAgZGlzdGluY3QoUGxvdE9ic2VydmF0aW9uSUQsIGRhdGUsIC5rZWVwX2FsbCA9IFRSVUUpICU+JQ0KICAjIFJlbW92ZSB1bndhbnRlZCBjb2x1bW5zDQogIHNlbGVjdCgtYHN5c3RlbTppbmRleGAsIC1pbWFnZV9pZCwgLS5nZW8sIC10aW1lX3V0YywgLXRpbWVzdGFtcCwNCiAgICAgICAgIFgxLCBYMiwgWDMpICU+JQ0KICAjIEpvaW4NCiAgbGVmdF9qb2luKG5fZGF5cykNCmBgYA0KDQojIENhbGN1bGF0ZSBpbmRpY2VzDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQojIENhbGN1bGF0ZSBpbmRpY2VzDQpkYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcyA8LSBkYXRhX1JTX0xhbmRzYXRfYmFuZHNfdXBkYXRlZCAlPiUNCiAgIyBTZXQgUGxvdE9ic2VydmF0aW9uSUQgYXMgZmFjdG9yDQogIG11dGF0ZShQbG90T2JzZXJ2YXRpb25JRCA9IGZhY3RvcihQbG90T2JzZXJ2YXRpb25JRCkpICU+JQ0KICAjIFJlbmFtZSB0aGUgYmFuZHMNCiAgcmVuYW1lKGJsdWUgPSBCbHVlLCBncmVlbiA9IEdyZWVuLCByZWQgPSBSZWQsIE5JUiA9IE5JUiwgU1dJUiA9IFNXSVIxKSAlPiUNCiAgIyBTY2FsZSB0aGUgYmFuZHMNCiAgbXV0YXRlKGJsdWUgPSBibHVlIC8gMTAwMDAsIGdyZWVuID0gZ3JlZW4gLyAxMDAwMCwgcmVkID0gcmVkIC8gMTAwMDAsDQogICAgICAgICBOSVIgPSBOSVIgLyAxMDAwMCwgU1dJUiA9IFNXSVIgLyAxMDAwMCkgJT4lDQogICMgQ3JlYXRlIGNvbHVtbiB0aGF0IGNvbWJpbmVzIHRoZSBkYXkgb2YgdGhlIG1vbnRoIGFuZCB0aGUgdGltZQ0KICBtdXRhdGUoDQogICAgZGF0ZSA9IGFzLlBPU0lYY3QoZGF0ZSksDQogICAgIyBOb3JtYWxpemUgdGhlIGRhdGVzIHRvIGEgZml4ZWQgeWVhciAoMjAwMCkNCiAgICAjIHNvIHRoYXQgc2Vhc29uYWwgcGF0dGVybnMgYWNyb3NzIGRpZmZlcmVudCB5ZWFycyBjYW4gYmUgY29tcGFyZWQgdmlzdWFsbHkNCiAgICBkYXlfbW9udGggPSBhcy5QT1NJWGN0KGZvcm1hdChkYXRlLCAiMjAwMC0lbS0lZCIpKSkgJT4lDQogICMgQ3JlYXRlIGNvbHVtbiB3aXRoIERPWQ0KICBtdXRhdGUoRE9ZID0geWRheShkYXRlKSkgJT4lDQogICMgQ2FsY3VsYXRlIE5EVkkNCiAgbXV0YXRlKE5EVkkgPSAoTklSIC0gcmVkKSAvIChOSVIgKyByZWQpLA0KICAgICAgICAgRVZJID0gKE5JUiAtIHJlZCkgKiAyLjUgLyAoTklSICsgNiAqIHJlZCAtIDcuNSAqIGJsdWUgKyAxKSwNCiAgICAgICAgIFNBVkkgPSAoTklSIC0gcmVkKSAqIDEuNSAvIChOSVIgKyByZWQgKyAwLjUpLA0KICAgICAgICAgTkRNSSA9IChOSVIgLSBTV0lSKSAvIChOSVIgKyBTV0lSKSwNCiAgICAgICAgIE5EV0kgPSAoZ3JlZW4gLSBOSVIpIC8gKGdyZWVuICsgTklSKSkgJT4lDQogICMgU2V0dGluZyB2YWx1ZXMgb2YgaW5kaWNlcyBvdXRzaWRlIGV4cGVjdGVkIHJhbmdlcyAoZXJyb3JzKSB0byBOQQ0KICBtdXRhdGUoRVZJID0gaWZfZWxzZShFVkkgPiAxIHwgRVZJIDwgLTEsIE5BLCBFVkkpKSAjIDEyOTM5MTIgdmFsdWVzIG9mIEVWSSBhcyBOQSENCmBgYA0KDQpTYXZlOg0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kc2F2ZShkYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcywgZmlsZSA9ICJvYmplY3RzL2RhdGFfUlNfTGFuZHNhdF9iYW5kc19pbmRpY2VzLlJkYXRhIikNCmBgYA0KDQpQbG90IG5fZGF5dGltZToNCg0KYGBge3J9DQpkYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcyAlPiUNCiAgZ3JvdXBfYnkoUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICBzdW1tYXJpc2Uobl9kYXlzID0gZmlyc3Qobl9kYXlzKSkgJT4lIHVuZ3JvdXAoKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gbl9kYXlzKSkgKyBnZW9tX2hpc3RvZ3JhbShjb2xvciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMgQ29tcHV0ZSBwaGVub2xvZ2ljYWwgbWV0cmljcyBmcm9tIG1vZGVscyBmaXR0ZWQgdG8gdGltZSBzZXJpZXMgZGF0YQ0KDQojIyBGdW5jdGlvbg0KDQpVc2luZyBHQU1zLCByZXdlaWdodGluZyBhbmQgMyBpdGVyYXRpb25zLg0KDQpVc2luZyBib3RoIGEgY2hhbmdlIGRldGVjdGlvbiBtZXRob2QgKG1heGltdW0gc2xvcGUpIGFuZCBhIHRocmVzaG9sZCBtZXRob2QgKDUwJSBhbXBsaXR1ZGUpIHRvIGNhbGN1bGF0ZSBzb3MgYW5kIGVvcy4NCg0KQXBwcm9hY2ggc2ltaWxhciB0byBodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmphZy4yMDIwLjEwMjE3MiBmb3IgR0FNIGZpdHRpbmcgYW5kIGNoYW5nZSBkZXRlY3Rpb24gbWV0aG9kLCBhbmQgdG8gaHR0cHM6Ly93d3cubWRwaS5jb20vMjA3Mi00MjkyLzEyLzIyLzM3MzggZm90IHRocmVzaG9sZCBtZXRob2QuDQoNCkRlZmluZSBmdW5jdGlvbiB0byBjb21wdXRlIHBoZW5vbG9neSBtZXRyaWNzIHVzaW5nIEdBTSBmaXQgYW5kIE5EVkkgLyBFVkkgLyBTQVZJOg0KDQpgYGB7cn0NCmNvbXB1dGVfbWV0cmljc19tb2RlbHMgPC0gZnVuY3Rpb24oZGYsIGluZGV4X2NvbHMgPSBjKCJORFZJIiwgIkVWSSIsICJTQVZJIikpIHsNCiAgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsNCiAgICBsaWJyYXJ5KG1nY3YpDQogICAgbGlicmFyeShubG1lKQ0KICAgIH0pDQogIA0KICBwbGFuKG11bHRpc2Vzc2lvbikgICMgU2V0IHVwIHBhcmFsbGVsIHByb2Nlc3NpbmcNCiAgDQogICMgQ3JlYXRlIGEgbGlzdCBvZiBpbmRleC1zcGVjaWZpYyBkYXRhIGZyYW1lcw0KICBpbmRleF9kZnMgPC0gbGFwcGx5KGluZGV4X2NvbHMsIGZ1bmN0aW9uKGluZGV4X2NvbCkgew0KICAgIGxpc3QoaW5kZXhfY29sID0gaW5kZXhfY29sLCBkZiA9IGRmICU+JQ0KICAgICAgICAgICBzZWxlY3QoRE9ZLCBQbG90T2JzZXJ2YXRpb25JRCwgYWxsX29mKGluZGV4X2NvbCkpKQ0KICAgIH0pDQogIA0KICAjIERlZmluZSB0aGUgcHJvY2Vzc2luZyBmdW5jdGlvbiBmb3IgZWFjaCBpbmRleA0KICBwcm9jZXNzX2luZGV4IDwtIGZ1bmN0aW9uKGluZGV4X2RhdGEpIHsNCiAgICBpbmRleF9jb2wgPC0gaW5kZXhfZGF0YSRpbmRleF9jb2wNCiAgICBkZl9pbmRleCA8LSBpbmRleF9kYXRhJGRmICU+JQ0KICAgICAgZmlsdGVyKCFpcy5uYSguZGF0YVtbaW5kZXhfY29sXV0pKSAlPiUNCiAgICAgIGFycmFuZ2UoRE9ZKQ0KICAgIA0KICAgIHBsb3RfaWQgPC0gdW5pcXVlKGRmX2luZGV4JFBsb3RPYnNlcnZhdGlvbklEKQ0KDQogICAgaWYgKG5yb3coZGZfaW5kZXgpIDwgMTApIHsNCiAgICAgIG1lc3NhZ2UoIiAgU2tpcHBlZDogaW5zdWZmaWNpZW50IGRhdGEgKDwgMTAgcm93cykiKQ0KICAgICAgcmV0dXJuKHRpYmJsZShQbG90T2JzZXJ2YXRpb25JRCA9IHBsb3RfaWQsIGluZGV4ID0gaW5kZXhfY29sLA0KICAgICAgICAgICAgICAgICAgICBzb3Nfc2xvcGUgPSBOQV9yZWFsXywgc29zX3RocmVzaG9sZCA9IE5BX3JlYWxfLA0KICAgICAgICAgICAgICAgICAgICBwb3MgPSBOQV9yZWFsXywgZW9zX3Nsb3BlID0gTkFfcmVhbF8sIA0KICAgICAgICAgICAgICAgICAgICBlb3NfdGhyZXNob2xkID0gTkFfcmVhbF8sIGF1Y19zbG9wZSA9IE5BX3JlYWxfLA0KICAgICAgICAgICAgICAgICAgICBhdWNfdGhyZXNob2xkID0gTkFfcmVhbF8sIFZtYXggPSBOQV9yZWFsXywNCiAgICAgICAgICAgICAgICAgICAgRE9ZID0gZGZfaW5kZXgkRE9ZLCB2YWx1ZSA9IE5BX3JlYWxfKSkNCiAgICB9DQogICAgDQogICAgIyBSZXBsYWNlIGVhcmx5L2xhdGUgRE9ZIHZhbHVlcw0KICAgIGJhc2VfdmFsdWVfZWFybHkgPC0gbWVhbihkZl9pbmRleCAlPiUgZmlsdGVyKERPWSA8PSA1MCkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bGwoaW5kZXhfY29sKSwgbmEucm0gPSBUUlVFKQ0KICAgIGJhc2VfdmFsdWVfbGF0ZSAgPC0gbWVhbihkZl9pbmRleCAlPiUgZmlsdGVyKERPWSA+PSAzMTUpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKGluZGV4X2NvbCksIG5hLnJtID0gVFJVRSkNCg0KICAgIGRmX2luZGV4IDwtIGRmX2luZGV4ICU+JQ0KICAgICAgbXV0YXRlKCEhaW5kZXhfY29sIDo9IGNhc2Vfd2hlbigNCiAgICAgICAgRE9ZIDw9IDUwIH4gYmFzZV92YWx1ZV9lYXJseSwNCiAgICAgICAgRE9ZID49IDMxNSB+IGJhc2VfdmFsdWVfbGF0ZSwNCiAgICAgICAgVFJVRSB+IC5kYXRhW1tpbmRleF9jb2xdXQ0KICAgICAgKSkNCg0KICAgIHggPC0gZGZfaW5kZXgkRE9ZDQogICAgeSA8LSBkZl9pbmRleFtbaW5kZXhfY29sXV0NCiAgICB3ZWlnaHRzIDwtIHJlcCgxLCBsZW5ndGgoeSkpDQogICAgDQogICAgIyBHQU0gZml0DQogICAgcHJlZCA8LSBOVUxMDQogICAgZm9yIChpIGluIDE6Mykgew0KICAgICAgZ2FtX2ZpdCA8LSB0cnlDYXRjaCh7DQogICAgICAgIG1nY3Y6OmJhbSh5IH4gcyh4LCBicyA9ICJ0cCIpLHdlaWdodHMgPSB3ZWlnaHRzKQ0KICAgICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgICBtZXNzYWdlKCIgIEdBTSBmaXR0aW5nIGZhaWxlZCBmb3IgIiwgcGxvdF9pZCwgIiAtICIsIGluZGV4X2NvbCwgIjogIiwgDQogICAgICAgICAgICAgICAgICBlJG1lc3NhZ2UpDQogICAgICAgICAgcmV0dXJuKE5VTEwpDQogICAgICAgICAgfSkNCiAgICAgIGlmIChpcy5udWxsKGdhbV9maXQpKSB7DQogICAgICAgIHJldHVybih0aWJibGUoDQogICAgICAgICAgUGxvdE9ic2VydmF0aW9uSUQgPSBwbG90X2lkLA0KICAgICAgICAgIGluZGV4ID0gaW5kZXhfY29sLA0KICAgICAgICAgIHNvc19zbG9wZSA9IE5BX3JlYWxfLA0KICAgICAgICAgIHNvc190aHJlc2hvbGQgPSBOQV9yZWFsXywNCiAgICAgICAgICBwb3MgPSBOQV9yZWFsXywNCiAgICAgICAgICBlb3Nfc2xvcGUgPSBOQV9yZWFsXywgDQogICAgICAgICAgZW9zX3RocmVzaG9sZCA9IE5BX3JlYWxfLCANCiAgICAgICAgICBhdWNfc2xvcGUgPSBOQV9yZWFsXywNCiAgICAgICAgICBhdWNfdGhyZXNob2xkID0gTkFfcmVhbF8sIA0KICAgICAgICAgIFZtaW5fcHJlID0gTkFfcmVhbF8sIA0KICAgICAgICAgIFZtaW5fcG9zdCA9IE5BX3JlYWxfLA0KICAgICAgICAgIFZtYXggPSBOQV9yZWFsXywgDQogICAgICAgICAgdV9zb3MgPSBOQV9yZWFsXywgDQogICAgICAgICAgdV9lb3MgPSBOQV9yZWFsXywNCiAgICAgICAgICBET1kgPSBkZl9pbmRleCRET1ksDQogICAgICAgICAgdmFsdWUgPSBOQV9yZWFsXykpDQogICAgICAgIH0NCiAgICAgIA0KICAgICAgcHJlZCA8LSB0cnlDYXRjaCh7DQogICAgICAgIHByZWRpY3QoZ2FtX2ZpdCwgbmV3ZGF0YSA9IHRpYmJsZSh4ID0geCkpDQogICAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgICAgICAgIG1lc3NhZ2UoIlByZWRpY3Rpb24gZmFpbGVkIGZvciAiLCBwbG90X2lkLCAiIC0gIiwgaW5kZXhfY29sLCAiOiAiLA0KICAgICAgICAgICAgICAgICAgZSRtZXNzYWdlKQ0KICAgICAgICAgIHJldHVybihyZXAoTkFfcmVhbF8sIGxlbmd0aCh4KSkpDQogICAgICAgICAgfSkNCiAgICAgIA0KICAgICAgaWR4X2JldHdlZW4gPC0gd2hpY2goeCA+IDUwICYgeCA8IDMxNSAmICFpcy5uYShwcmVkKSAmIHByZWQgIT0gMCkNCiAgICAgIHdlaWdodHMgPC0gcmVwKDEsIGxlbmd0aCh5KSkNCiAgICAgIHdlaWdodHNbaWR4X2JldHdlZW5dIDwtICh5W2lkeF9iZXR3ZWVuXSAvIChwcmVkW2lkeF9iZXR3ZWVuXSArIDFlLTYpKV40DQogICAgICB3ZWlnaHRzW3dlaWdodHMgPiAxIHwgaXMubmEod2VpZ2h0cyldIDwtIDENCiAgICAgIH0NCiAgICANCiAgICAjIENvbXB1dGUgbWV0cmljcw0KICAgIHNsb3BlIDwtIGMoTkEsIGRpZmYocHJlZCkpDQogICAgaWR4IDwtIHdoaWNoKHggPj0gNTAgJiB4IDw9IDMxNSkNCiAgICBwb3MgPC0gaWYgKGxlbmd0aChpZHgpID4gMCkgeFtpZHhdW3doaWNoLm1heChwcmVkW2lkeF0pXSBlbHNlIE5BX3JlYWxfDQoNCiAgICBzb3Nfc2xvcGUgPC0gaWYgKCFpcy5uYShwb3MpKSB7DQogICAgICBpZHggPC0gd2hpY2goeCA8IHBvcykNCiAgICAgIGlmIChsZW5ndGgoaWR4KSA+IDApIHhbaWR4XVt3aGljaC5tYXgoc2xvcGVbaWR4XSldIGVsc2UgTkFfcmVhbF8NCiAgICB9IGVsc2UgTkFfcmVhbF8NCg0KICAgIGVvc19zbG9wZSA8LSBpZiAoIWlzLm5hKHBvcykpIHsNCiAgICAgIGlkeCA8LSB3aGljaCh4ID4gcG9zKQ0KICAgICAgaWYgKGxlbmd0aChpZHgpID4gMCkgeFtpZHhdW3doaWNoLm1pbihzbG9wZVtpZHhdKV0gZWxzZSBOQV9yZWFsXw0KICAgIH0gZWxzZSBOQV9yZWFsXw0KDQogICAgaW50ZWdyYXRpb25faWR4X3Nsb3BlIDwtIHdoaWNoKHggPj0gc29zX3Nsb3BlICYgeCA8PSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlb3Nfc2xvcGUgJiAhaXMubmEocHJlZCkpDQogICAgYXVjX3Nsb3BlIDwtIGlmIChsZW5ndGgoaW50ZWdyYXRpb25faWR4X3Nsb3BlKSA+IDEpIHsNCiAgICAgIHN1bShkaWZmKHhbaW50ZWdyYXRpb25faWR4X3Nsb3BlXSkgKiANCiAgICAgICAgICAgIHpvbzo6cm9sbG1lYW4ocHJlZFtpbnRlZ3JhdGlvbl9pZHhfc2xvcGVdLCAyKSkNCiAgICAgIH0gZWxzZSBOQV9yZWFsXw0KICAgIA0KICAgICMgVm1pbiBhbnRlcyB5IGRlc3B1w6lzIGRlbCBwaWNvDQogICAgVm1pbl9wcmUgPC0gaWYgKCFpcy5uYShwb3MpKSBtaW4ocHJlZFt4IDw9IHBvc10sIG5hLnJtID0gVFJVRSllbHNlIE5BX3JlYWxfDQogICAgVm1pbl9wb3N0IDwtIGlmICghaXMubmEocG9zKSkgbWluKHByZWRbeCA+PSBwb3NdLCBuYS5ybSA9IFRSVUUpIGVsc2UgTkFfcmVhbF8NCiAgICBWbWF4IDwtIG1heChwcmVkLCBuYS5ybSA9IFRSVUUpDQogICAgDQogICAgIyBVbWJyYWxlcyByZWxhdGl2b3MNCiAgICBwIDwtIDAuNQ0KICAgIHVfc29zIDwtIGlmICghaXMubmEoVm1pbl9wcmUpKSBWbWluX3ByZSArIHAgKiAoVm1heCAtIFZtaW5fcHJlKSBlbHNlIE5BX3JlYWxfDQogICAgdV9lb3MgPC0gaWYgKCFpcy5uYShWbWluX3Bvc3QpKSBWbWluX3Bvc3QgKyBwICogKFZtYXggLSBWbWluX3Bvc3QpIGVsc2UgTkFfcmVhbF8NCiAgICANCiAgICAjIERPWSBkb25kZSBzZSBjcnV6YW4gbG9zIHVtYnJhbGVzDQogICAgc29zX3RocmVzaG9sZCA8LSBpZiAoIWlzLm5hKHVfc29zKSAmJiAhaXMubmEocG9zKSkgew0KICAgICAgY2FuZGlkYXRlcyA8LSB4W3doaWNoKHByZWQgPj0gdV9zb3MgJiB4IDwgcG9zKV0NCiAgICAgIGlmIChsZW5ndGgoY2FuZGlkYXRlcykgPiAwKSBjYW5kaWRhdGVzWzFdIGVsc2UgTkFfcmVhbF8NCiAgICAgIH0gZWxzZSBOQV9yZWFsXw0KICAgIA0KICAgIGVvc190aHJlc2hvbGQgPC0gaWYgKCFpcy5uYSh1X2VvcykgJiYgIWlzLm5hKHBvcykpIHsNCiAgICAgIGNhbmRpZGF0ZXMgPC0geFt3aGljaChwcmVkID49IHVfZW9zICYgeCA+IHBvcyldDQogICAgICBpZiAobGVuZ3RoKGNhbmRpZGF0ZXMpID4gMCkgcmV2KGNhbmRpZGF0ZXMpWzFdIGVsc2UgTkFfcmVhbF8NCiAgICAgIH0gZWxzZSBOQV9yZWFsXw0KICAgIA0KICAgIGludGVncmF0aW9uX2lkeF90aHJlc2hvbGQgPC0gd2hpY2goeCA+PSBzb3NfdGhyZXNob2xkICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPD0gZW9zX3RocmVzaG9sZCAmICFpcy5uYShwcmVkKSkNCiAgICBhdWNfdGhyZXNob2xkIDwtIGlmIChsZW5ndGgoaW50ZWdyYXRpb25faWR4X3RocmVzaG9sZCkgPiAxKSB7DQogICAgICBzdW0oZGlmZih4W2ludGVncmF0aW9uX2lkeF90aHJlc2hvbGRdKSAqIA0KICAgICAgICAgICAgem9vOjpyb2xsbWVhbihwcmVkW2ludGVncmF0aW9uX2lkeF90aHJlc2hvbGRdLCAyKSkNCiAgICAgIH0gZWxzZSBOQV9yZWFsXw0KICAgIA0KICAgICMgMS4gUHJlZGljY2lvbmVzIHBvciBET1kNCiAgICBmaXRzX2RmIDwtIHRpYmJsZSgNCiAgICAgIFBsb3RPYnNlcnZhdGlvbklEID0gdW5pcXVlKGRmX2luZGV4JFBsb3RPYnNlcnZhdGlvbklEKSwNCiAgICAgIERPWSA9IHgsDQogICAgICB2YWx1ZSA9IHByZWQsDQogICAgICBpbmRleCA9IGluZGV4X2NvbA0KICAgICAgKQ0KICAgIA0KICAgICMgMi4gTcOpdHJpY2FzIHJlc3VtZW4NCiAgICBtZXRyaWNzX2RmIDwtIHRpYmJsZSgNCiAgICAgIFBsb3RPYnNlcnZhdGlvbklEID0gdW5pcXVlKGRmX2luZGV4JFBsb3RPYnNlcnZhdGlvbklEKSwNCiAgICAgIGluZGV4ID0gaW5kZXhfY29sLA0KICAgICAgc29zX3Nsb3BlID0gc29zX3Nsb3BlLA0KICAgICAgc29zX3RocmVzaG9sZCA9IHNvc190aHJlc2hvbGQsDQogICAgICBwb3MgPSBwb3MsDQogICAgICBlb3Nfc2xvcGUgPSBlb3Nfc2xvcGUsDQogICAgICBlb3NfdGhyZXNob2xkID0gZW9zX3RocmVzaG9sZCwNCiAgICAgIGF1Y19zbG9wZSA9IGF1Y19zbG9wZSwNCiAgICAgIGF1Y190aHJlc2hvbGQgPSBhdWNfdGhyZXNob2xkLA0KICAgICAgVm1pbl9wcmUgPSBWbWluX3ByZSwNCiAgICAgIFZtaW5fcG9zdCA9IFZtaW5fcG9zdCwNCiAgICAgIFZtYXggPSBWbWF4LA0KICAgICAgdV9zb3MgPSB1X3NvcywNCiAgICAgIHVfZW9zID0gdV9lb3MNCiAgICAgICkNCiAgICANCiAgICAjIDMuIFVuaXIgcG9yIFBsb3RPYnNlcnZhdGlvbklELCBpbmRleA0KICAgIGZpbmFsX2RmIDwtIGxlZnRfam9pbihmaXRzX2RmLCBtZXRyaWNzX2RmLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJQbG90T2JzZXJ2YXRpb25JRCIsICJpbmRleCIpKQ0KICB9DQogIA0KICAjIFJ1biBpbiBwYXJhbGxlbA0KICByZXN1bHRzIDwtIGZ1dHVyZV9tYXAoaW5kZXhfZGZzLCBwcm9jZXNzX2luZGV4LCAucHJvZ3Jlc3MgPSBUUlVFKQ0KICByZXN1bHRzIDwtIHB1cnJyOjpjb21wYWN0KHJlc3VsdHMpICAjIHJlbW92ZXMgTlVMTHMNCiAgaWYgKGxlbmd0aChyZXN1bHRzKSA9PSAwKSByZXR1cm4odGliYmxlKCkpICAjIG9yIHJldHVybihOVUxMKQ0KICBiaW5kX3Jvd3MocmVzdWx0cykNCn0NCmBgYA0KDQojIyBDYWxjdWxhdGlvbg0KDQpBcHBseSB0aGUgZnVuY3Rpb24gd2l0aCBiYXRjaCBwcm9jZXNzaW5nLg0KDQpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KcGxhbihtdWx0aXNlc3Npb24sIHdvcmtlcnMgPSBhdmFpbGFibGVDb3JlcygpIC0gMSkNCg0KaWRzIDwtIHVuaXF1ZShkYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcyRQbG90T2JzZXJ2YXRpb25JRCkNCmJhdGNoZXMgPC0gc3BsaXQoaWRzLCBjZWlsaW5nKHNlcV9hbG9uZyhpZHMpIC8gNTApKSAgIyBiYXRjaGVzIG9mIDUwDQoNCnN0YXJ0X3RvdGFsIDwtIFN5cy50aW1lKCkNCg0KR0FNX2RhdGEgPC0gbWFwX2RmcihzZXFfYWxvbmcoYmF0Y2hlcyksIGZ1bmN0aW9uKGkpIHsNCiAgYmF0Y2hfaWRzIDwtIGJhdGNoZXNbW2ldXQ0KICB0b3RhbF9iYXRjaGVzIDwtIGxlbmd0aChiYXRjaGVzKQ0KICBiYXRjaF9maWxlIDwtIGZpbGUucGF0aCgib2JqZWN0cy9HQU1fYmF0Y2hlc19MYW5kc2F0IiwgcGFzdGUwKCJiYXRjaF8iLCBpLCAiLnJkcyIpKQ0KDQogIGlmIChmaWxlLmV4aXN0cyhiYXRjaF9maWxlKSkgew0KICAgIG1lc3NhZ2UoIuKchSBCYXRjaCAgIiwgaSwgIiBvZiAiLCB0b3RhbF9iYXRjaGVzLCANCiAgICAgICAgICAgICIgYWxyZWFkeSBwcm9jZXNzZWQuIExvYWRpbmcgZnJvbSBmaWxlLiIpDQogICAgcmV0dXJuKHJlYWRSRFMoYmF0Y2hfZmlsZSkpDQogIH0NCg0KICBtZXNzYWdlKCLwn5SEIFByb2Nlc3NpbmcgYmF0Y2ggICIsIGksICIgb2YgIiwgdG90YWxfYmF0Y2hlcywgIiB3aXRoICIsDQogICAgICAgICAgbGVuZ3RoKGJhdGNoX2lkcyksICIgSURzLi4uIikNCg0KICBzdGFydF9iYXRjaCA8LSBTeXMudGltZSgpDQoNCiAgcmVzdWx0IDwtIGRhdGFfUlNfTGFuZHNhdF9iYW5kc19pbmRpY2VzICU+JQ0KICAgIGZpbHRlcihQbG90T2JzZXJ2YXRpb25JRCAlaW4lIGJhdGNoX2lkcykgJT4lDQogICAgZ3JvdXBfc3BsaXQoUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICAgIHNldF9uYW1lcyhtYXBfY2hyKC4sIH4gYXMuY2hhcmFjdGVyKHVuaXF1ZSgueCRQbG90T2JzZXJ2YXRpb25JRCkpKSkgJT4lDQogICAgZnV0dXJlX21hcF9kZnIofiBjb21wdXRlX21ldHJpY3NfbW9kZWxzKGRmID0gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXhfY29scyA9IGMoIk5EVkkiLCAiRVZJIiwgIlNBVkkiKSksDQogICAgICAgICAgICAgICAgICAgLnByb2dyZXNzID0gVFJVRSkNCg0KICBlbmRfYmF0Y2ggPC0gU3lzLnRpbWUoKQ0KICBkdXJhdGlvbiA8LSByb3VuZChkaWZmdGltZShlbmRfYmF0Y2gsIHN0YXJ0X2JhdGNoLCB1bml0cyA9ICJtaW5zIiksIDIpDQogIG1lc3NhZ2UoIuKPse+4jyBCYXRjaCB0aW1lICIsIGksICI6ICIsIGR1cmF0aW9uLCAiIG1pbnV0ZXMiKQ0KDQogIG1lc3NhZ2UoIvCfkr4gU2F2aW5nIGJhdGNoICIsIGksICIgdG8gZmlsZS4uLiIpDQogIHNhdmVSRFMocmVzdWx0LCBiYXRjaF9maWxlKQ0KICBtZXNzYWdlKCLinIUgQmF0Y2ggIiwgaSwgIiBzYXZlZC4iKSANCg0KICByZXN1bHQNCn0pDQoNCmVuZF90b3RhbCA8LSBTeXMudGltZSgpDQp0b3RhbF90aW1lIDwtIHJvdW5kKGRpZmZ0aW1lKGVuZF90b3RhbCwgc3RhcnRfdG90YWwsIHVuaXRzID0gIm1pbnMiKSwgMikNCm1lc3NhZ2UoIuKPse+4jyBUb3RhbCB0aW1lOiAiLCB0b3RhbF90aW1lLCAiIG1pbnV0ZXMiKQ0KYGBgDQoNCmBgYHtyfQ0KcGxhbihzZXF1ZW50aWFsKQ0KYGBgDQoNCiMjIFNhdmUNCg0KTG9vazoNCg0KYGBge3J9DQpHQU1fZGF0YQ0KYGBgDQoNClNhdmUgYXMgYW4gb2JqZWN0Og0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kc2F2ZShHQU1fZGF0YSwgZmlsZSA9ICJvYmplY3RzL0dBTV9kYXRhX0xhbmRzYXQuUmRhdGEiKQ0KYGBgDQoNCiMjIEV4dHJhY3QgYXZlcmFnZSB2YWx1ZXMgb2YgaW5kaWNlcyBwZXIgbW9udGgNCg0KYGBge3J9DQpleHRyYWN0X21vbnRobHlfYXZnX2luZGljZXMgPC0gZnVuY3Rpb24oDQogICAgR0FNX2RhdGEsIA0KICAgIG1vbnRobHlfZG95cyA9IGxpc3QoIjAxIiA9IDE6MzEsICIwMiIgPSAzMjo1OSwgIjAzIiA9IDYwOjkwLCAiMDQiID0gOTE6MTIwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICIwNSIgPSAxMjE6MTUxLCAiMDYiID0gMTUyOjE4MSwgIjA3IiA9IDE4MjoyMTIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIjA4IiA9IDIxMzoyNDMsICIwOSIgPSAyNDQ6MjczLCAiMTAiID0gMjc0OjMwNCwNCiAgICAgICAgICAgICAgICAgICAgICAgICIxMSIgPSAzMDU6MzM0LCAiMTIiID0gMzM1OjM2NSkpIHsNCiAgR0FNX2RhdGEgJT4lDQogICAgbXV0YXRlKG1vbnRoID0gcHVycnI6Om1hcF9jaHIoRE9ZLCBmdW5jdGlvbihkb3kpIHsNCiAgICAgIG1vbnRoX25hbWUgPC0gbmFtZXMobW9udGhseV9kb3lzKVtzYXBwbHkobW9udGhseV9kb3lzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24ocikgZG95ICVpbiUgcildDQogICAgICBpZiAobGVuZ3RoKG1vbnRoX25hbWUpID4gMCkgbW9udGhfbmFtZSBlbHNlIE5BX2NoYXJhY3Rlcl8NCiAgICB9KSkgJT4lDQogICAgZmlsdGVyKCFpcy5uYShtb250aCkpICU+JQ0KICAgIGdyb3VwX2J5KFBsb3RPYnNlcnZhdGlvbklELCBpbmRleCwgbW9udGgpICU+JQ0KICAgIHN1bW1hcmlzZShhdmdfdmFsdWUgPSBtZWFuKHZhbHVlLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgICBtdXRhdGUoYXZnX3ZhbHVlID0gaWZlbHNlKGlzLmluZmluaXRlKGF2Z192YWx1ZSksIE5BLCBhdmdfdmFsdWUpKSAlPiUNCiAgICBhcnJhbmdlKFBsb3RPYnNlcnZhdGlvbklELCBtYXRjaChtb250aCwgbmFtZXMobW9udGhseV9kb3lzKSkpICU+JQ0KICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBtb250aCwgdmFsdWVzX2Zyb20gPSBhdmdfdmFsdWUsDQogICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gImF2Z192YWx1ZV8iKQ0KfQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQptb250aGx5X2F2Z19pbmRpY2VzIDwtIGV4dHJhY3RfbW9udGhseV9hdmdfaW5kaWNlcyhHQU1fZGF0YSkNCmBgYA0KDQpTYXZlIGFzIGFuIG9iamVjdDoNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnNhdmUobW9udGhseV9hdmdfaW5kaWNlcywgZmlsZSA9ICJvYmplY3RzL21vbnRobHlfYXZnX2luZGljZXNfTGFuZHNhdC5SZGF0YSIpDQpgYGANCg0KIyMgQXNzZXNzIHRpbWUgc2VyaWVzIHF1YWxpdHkNCg0KRm9yIHRoZSB0aW1lIHNlcmllcyB0byBiZSBhY2NlcHRhYmxlLCBpdCBzaG91bGQgaGF2ZSBhIHJlYXNvbmFibGUgbnVtYmVyIG9mIHRpbWUgcG9pbnRzLCBhbmQgdGhlc2UgcG9pbnRzIHNob3VsZCBiZSBkaXN0cmlidXRlZCBhbG9uZyBhbG1vc3QgYWxsIG1vbnRocyAoY291bGQgYmUgb2sgdG8gbWlzcyB0aGUgd2ludGVyIG1vbnRocykuDQoNCkluIEdBTSBkYXRhLCBjaGVjayBob3cgbWFueSB0aW1lIHBvaW50cyBhcmUgdGhlcmUgZm9yIGVhY2ggUGxvdE9ic2VydmF0aW9uSUQsIGhvdyBtYW55IG1vbnRocywgYW5kIHdoaWNoIG1vbnRocyBhcmUgbWlzc2luZy4NCg0KYGBge3J9DQp0c19xdWFsaXR5IDwtIEdBTV9kYXRhICU+JQ0KICAjIEZpbHRlciBvbmx5IE5EVkkgKGFsbCBpbmRpY2VzIHdpbGwgaGF2ZSB0aGUgc2FtZSB0aW1lIHBvaW50cykNCiAgZHBseXI6OmZpbHRlcihpbmRleCA9PSAiTkRWSSIpICU+JQ0KICAjIEdldCBtb250aCBmcm9tIERPWQ0KICBtdXRhdGUobW9udGggPSBtb250aCh5bWQoIjIwMjAtMDEtMDEiKSArIGRheXMoRE9ZIC0gMSkpKSAlPiUNCiAgIyBGb3IgZWFjaCBQbG90T2JzZXJ2YXRpb25JRA0KICBncm91cF9ieShQbG90T2JzZXJ2YXRpb25JRCkgJT4lDQogICMgR2V0IHRoZSBudW1iZXIgb2YgdGltZSBwb2ludHMgKGRheXMpIGFuZCB0aGUgbnVtYmVyIG9mIG1vbnRocw0KICBzdW1tYXJpc2UoDQogICAgbl9kYXlzID0gbl9kaXN0aW5jdChET1kpLA0KICAgIG5fbW9udGhzID0gbl9kaXN0aW5jdChtb250aCksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApICU+JQ0KICBsZWZ0X2pvaW4oR0FNX2RhdGEgJT4lDQogICAgICAgICAgICAgICMgRmlsdGVyIG9ubHkgTkRWSQ0KICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKGluZGV4ID09ICJORFZJIikgJT4lDQogICAgICAgICAgICAgICMgR2V0IG1vbnRoIGZyb20gRE9ZDQogICAgICAgICAgICAgIG11dGF0ZShtb250aCA9IG1vbnRoKHltZCgiMjAyMC0wMS0wMSIpICsgZGF5cyhET1kgLSAxKSkpICU+JQ0KICAgICAgICAgICAgICAjIEdldCB1bmlxdWUgdmFsdWVzIG9mIFBsb3RPYnNlcnZhdGlvbklEIGFuZCBtb250aA0KICAgICAgICAgICAgICBkaXN0aW5jdChQbG90T2JzZXJ2YXRpb25JRCwgbW9udGgpICU+JQ0KICAgICAgICAgICAgICAjIEFkZCAxIGFzIHZhbHVlDQogICAgICAgICAgICAgIG11dGF0ZSh2YWx1ZSA9IDEpICU+JQ0KICAgICAgICAgICAgICAjIFJlc2hhcGUgdG8gd2lkZSBmb3JtYXQgYW5kIGFkZCB6ZXJvcyB3aGVuIG1vbnRoIGlzIG1pc3NpbmcNCiAgICAgICAgICAgICAgcGl2b3Rfd2lkZXIoDQogICAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IG1vbnRoLA0KICAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJtb250aCIsDQogICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSB2YWx1ZSwNCiAgICAgICAgICAgICAgICB2YWx1ZXNfZmlsbCA9IDApLA0KICAgICAgICAgICAgYnkgPSAiUGxvdE9ic2VydmF0aW9uSUQiKQ0KYGBgDQoNCkhpc3RvZ3JhbXMgdGltZSBwb2ludHMgYW5kIG4gbW9udGhzOg0KDQpgYGB7cn0NCmdncGxvdCh0c19xdWFsaXR5LCBhZXMoeCA9IG5fZGF5cykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAiYmxhY2siLCBmaWxsID0gIndoaXRlIikgKw0KICB4bGFiKCJOdW1iZXIgb2YgdGltZSBwb2ludHMgKGRheXMpIGluIHRoZSBMYW5kc2F0IHRpbWUgc2VyaWVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmdncGxvdCh0c19xdWFsaXR5LCBhZXMoeCA9IG5fbW9udGhzKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShjb2xvciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiKSArDQogIHhsYWIoIk51bWJlciBvZiBtb250aHMgaW4gdGhlIExhbmRzYXQgdGltZSBzZXJpZXMiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkNvdW50IGhvdyBtYW55IFBsb3RPYnNlcnZhdGlvbklEcyBoYXZlIG1pc3NpbmcgZGF0YSAodmFsdWUgMCkgZm9yIGVhY2ggbW9udGg6DQoNCmBgYHtyfQ0Kb2JzX21pc3NpbmdfbW9udGggPC0gdHNfcXVhbGl0eSAlPiUNCiAgc3VtbWFyaXNlKGFjcm9zcyhzdGFydHNfd2l0aCgibW9udGgiKSwgfiBzdW0oLnggPT0gMCkpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIm1vbnRoIiwgdmFsdWVzX3RvID0gIm5vYnNfbWlzc2luZyIpDQoNCmdncGxvdChvYnNfbWlzc2luZ19tb250aCAlPiUNCiAgICAgICAgIG11dGF0ZShtb250aCA9IGZhY3Rvcihtb250aCwgbGV2ZWxzID0gcGFzdGUwKCJtb250aCIsIDE6MTIpKSksIA0KICAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBub2JzX21pc3NpbmcpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIHlsYWIoIk51bWJlciBvZiBQbG90T2JzZXJ2YXRpb25JRCB3aXRoIG1pc3NpbmcgZGF0YSIpICsNCiAgZ2d0aXRsZSgiTWlzc2luZyBkYXRhIGluIFMyIHRpbWUgc2VyaWVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpBZGQgcXVhbGl0eSBmbGFnOg0KDQpgYGB7cn0NCnRzX3F1YWxpdHlfZmxhZyA8LSB0c19xdWFsaXR5ICU+JQ0KICByb3d3aXNlKCkgJT4lDQogIG11dGF0ZSgNCiAgICAjICBJZiAyIGNvbnNlY3V0aXZlIG1vbnRocyBvZiB0aGUgcGVyaW9kIE1hcmNoLU9jdG9iZXIgYXJlIG1pc3NpbmcNCiAgICAjIHF1YWxpdHlfZmxhZyA9IDANCiAgICBxdWFsaXR5X2ZsYWcgPSB7DQogICAgICBtb250aHMgPC0gY19hY3Jvc3MobW9udGgzOm1vbnRoMTApDQogICAgICBpZiAoYW55KG1vbnRoc1stbGVuZ3RoKG1vbnRocyldID09IDAgJiBtb250aHNbLTFdID09IDApKSAwIGVsc2UgMQ0KICAgIH0NCiAgKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3J9DQp0c19xdWFsaXR5X2ZsYWcgJT4lIGNvdW50KHF1YWxpdHlfZmxhZykNCmBgYA0KDQojIyBCb3hwbG90IGNvbXBhcmluZyBtb21lbnRzIGZvciBkaWZmZXJlbnQgaW5kaWNlcw0KDQpgYGB7cn0NCkdBTV9kYXRhICU+JSANCiAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBpbmRleCwgc29zX3Nsb3BlLCBzb3NfdGhyZXNob2xkLCBwb3MsIGVvc19zbG9wZSwNCiAgICAgICAgIGVvc190aHJlc2hvbGQpICU+JSBkaXN0aW5jdCgpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGMoc29zX3Nsb3BlLCBzb3NfdGhyZXNob2xkLCBwb3MsIGVvc19zbG9wZSwgZW9zX3RocmVzaG9sZCksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJtb21lbnQiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gbW9tZW50LCB5ID0gdmFsdWUsIGZpbGwgPSBpbmRleCkpICsgZ2VvbV9ib3hwbG90KCkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyBUQkQgaWYgbmVlZGVkOiBQbG90IGZpdCBhbmQgbW9tZW50cyBmb3IgZWFjaCBQbG90T2JzZXJ2YXRpb25JRA0KDQojIyMgUXVhbGl0eSA9IDENCg0KYGBge3J9DQojIEdldCB1bmlxdWUgSURzIHdpdGggcXVhbGl0eV9mbGFnID09IDENCmlkc19xMSA8LSB0c19xdWFsaXR5X2ZsYWcgJT4lDQogIGRwbHlyOjpmaWx0ZXIocXVhbGl0eV9mbGFnID09IDEpICU+JQ0KICBtdXRhdGUoUGxvdE9ic2VydmF0aW9uSUQgPSBkcm9wbGV2ZWxzKFBsb3RPYnNlcnZhdGlvbklEKSkgJT4lDQogIHB1bGwoUGxvdE9ic2VydmF0aW9uSUQpDQpHQU1fZGF0YV9pZHNfcTEgPC0gR0FNX2RhdGEgJT4lDQogICMgSm9pbiB0byBnZXQgYmlvZ2VvIGFuZCB1bml0DQogIGxlZnRfam9pbihkYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcyAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBiaW9nZW8sIHVuaXQpICU+JQ0KICAgICAgICAgICAgICBkaXN0aW5jdCgpKSAlPiUNCiAgIyBKb2luIHRvIGdldCBFVU5JUyBpbmZvDQogICBsZWZ0X2pvaW4oZGJfRXVyb3BhX2FsbG9icyAlPiUNCiAgICAgICAgICAgICAgIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgRVVOSVNhXzEsIEVVTklTYV8xX2Rlc2NyLA0KICAgICAgICAgICAgICAgICAgICAgIEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjcikpICU+JQ0KICAjIEpvaW4gdG8gZ2V0IG9yaWdpbmFsIHZhbHVlcyBvZiBpbmRpY2VzDQogIGxlZnRfam9pbihkYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcyAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBET1ksIE5EVkksIEVWSSwgU0FWSSkgJT4lDQogICAgICAgICAgICAgIHBpdm90X2xvbmdlcihjb2xzID0gYyhORFZJLCBFVkksIFNBVkkpLCBuYW1lc190byA9ICJpbmRleCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlX29yaWciKSkgJT4lDQogICMgSm9pbiB0byBnZXQgdHNfcXVhbGl0eSBkYXRhDQogIGxlZnRfam9pbih0c19xdWFsaXR5X2ZsYWcgJT4lIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgcXVhbGl0eV9mbGFnKSkgJT4lDQogICMgS2VlcCBvbmx5IHRob3NlIHdpdGggcXVhbGl0eV9mbGFnID09IDENCiAgZHBseXI6OmZpbHRlcihxdWFsaXR5X2ZsYWcgPT0gMSkNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBHZXQgdW5pcXVlIFBsb3RPYnNlcnZhdGlvbklEcw0KdW5pcXVlX2lkczEgPC0gaWRzX3ExDQoNCiMgQ3JlYXRlIGFuZCBzdG9yZSBwbG90cyBpbiBhIGxpc3QNCnRzX3Bsb3RzX3ExIDwtIG1hcCh1bmlxdWVfaWRzMSwgZnVuY3Rpb24oaWQpIHsNCiAgcGxvdF9kYXRhIDwtIEdBTV9kYXRhX2lkc19xMSAlPiUNCiAgICBtdXRhdGUoUGxvdE9ic2VydmF0aW9uSUQgPSBhcy5jaGFyYWN0ZXIoUGxvdE9ic2VydmF0aW9uSUQpKSAlPiUNCiAgICBkcGx5cjo6ZmlsdGVyKFBsb3RPYnNlcnZhdGlvbklEID09IGlkKSANCiAgDQogICMgRXh0cmFjdCBtZXRhZGF0YSBmb3IgdGl0bGUNCiAgbWV0YWRhdGEgPC0gcGxvdF9kYXRhICU+JQ0KICAgIHNlbGVjdChiaW9nZW8sIHVuaXQsIEVVTklTYV8xLCBFVU5JU2FfMiwgcXVhbGl0eV9mbGFnKSAlPiUNCiAgICBkaXN0aW5jdCgpDQogIA0KICBnZ3Bsb3QoKSArDQogICAgIyBSYXcgZGF0YSBwb2ludHMNCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwbG90X2RhdGEsYWVzKHggPSBET1ksIHkgPSB2YWx1ZV9vcmlnKSwgYWxwaGEgPSAwLjUpICsNCiAgICBnZW9tX2xpbmUoZGF0YSA9IHBsb3RfZGF0YSwgYWVzKHggPSBET1ksIHkgPSB2YWx1ZSksIA0KICAgICAgICAgICAgICBzaXplID0gMC41LCBjb2xvciA9ICJibHVlIikgKw0KICAgIGdlb21fdmxpbmUoZGF0YSA9IHBsb3RfZGF0YSAlPiUgZGlzdGluY3QoaW5kZXgsIHNvc19zbG9wZSksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IHNvc19zbG9wZSwgZ3JvdXAgPSBpbmRleCksDQogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMC41LCBjb2xvciA9ICJyZWQiKSArDQogICAgZ2VvbV92bGluZShkYXRhID0gcGxvdF9kYXRhICU+JSBkaXN0aW5jdChpbmRleCwgc29zX3RocmVzaG9sZCksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IHNvc190aHJlc2hvbGQsIGdyb3VwID0gaW5kZXgpLA0KICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDAuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICAgIGdlb21fdmxpbmUoZGF0YSA9IHBsb3RfZGF0YSAlPiUgZGlzdGluY3QoaW5kZXgsIHBvcyksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IHBvcywgZ3JvdXAgPSBpbmRleCksDQogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkb3R0ZWQiLCBzaXplID0gMC41LCBjb2xvciA9ICJibHVlIikgKw0KICAgIGdlb21fdmxpbmUoZGF0YSA9IHBsb3RfZGF0YSAlPiUgZGlzdGluY3QoaW5kZXgsIGVvc19zbG9wZSksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IGVvc19zbG9wZSwgZ3JvdXAgPSBpbmRleCksDQogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMC41LCBjb2xvciA9ICJyZWQiKSArDQogICAgZ2VvbV92bGluZShkYXRhID0gcGxvdF9kYXRhICU+JSBkaXN0aW5jdChpbmRleCwgZW9zX3RocmVzaG9sZCksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IGVvc190aHJlc2hvbGQsIGdyb3VwID0gaW5kZXgpLA0KICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDAuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICAgIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoaW5kZXgpKSArDQogICAgbGFicygNCiAgICAgIHRpdGxlID0gZ2x1ZTo6Z2x1ZSgie2lkfSB8IHttZXRhZGF0YSRiaW9nZW99e21ldGFkYXRhJHVuaXR9IHwge21ldGFkYXRhJEVVTklTYV8xfSB8IHttZXRhZGF0YSRFVU5JU2FfMn0gfCBRdWFsaXR5OiB7bWV0YWRhdGEkcXVhbGl0eV9mbGFnfSIpLA0KICAgICAgeCA9ICJEYXkgb2YgWWVhciIsDQogICAgICB5ID0gIkluZGV4IFZhbHVlIg0KICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KfSkNCg0KIyBOYW1lIHRoZSBsaXN0IGJ5IFBsb3RPYnNlcnZhdGlvbklEDQpuYW1lcyh0c19wbG90c19xMSkgPC0gdW5pcXVlX2lkczENCg0KIyBEaXNwbGF5IHRoZSBmaXJzdCBwbG90DQp0c19wbG90c19xMVsxXQ0KYGBgDQoNClNhdmUgZWFjaCBwbG90IHRvIGEgZmlsZToNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCndhbGsyKHRzX3Bsb3RzX3ExLCBzZXFfYWxvbmcodHNfcGxvdHNfcTEpLCB+IGdnc2F2ZSgNCiAgZmlsZW5hbWUgPSBwYXN0ZTAoIkM6L0FuYWx5c2VzL01PVElWQVRFX3ZhbGlkYXRpb24vb3V0cHV0L2ZpZ3VyZXMvcGhlbm9sb2d5L3RzX3ExX0xhbmRzYXQvdHNfcGxvdHNfcTEiLCAueSwgIi5qcGVnIiksDQogIHBsb3QgPSAueCwNCiAgd2lkdGggPSA4LA0KICBoZWlnaHQgPSA1DQopKQ0KYGBgDQoNCiMjIyBRdWFsaXR5ID0gMA0KDQpgYGB7cn0NCiMgR2V0IHVuaXF1ZSBJRHMgd2l0aCBxdWFsaXR5X2ZsYWcgPT0gMA0KaWRzX3EwIDwtIHRzX3F1YWxpdHlfZmxhZyAlPiUNCiAgZHBseXI6OmZpbHRlcihxdWFsaXR5X2ZsYWcgPT0gMCkgJT4lDQogIG11dGF0ZShQbG90T2JzZXJ2YXRpb25JRCA9IGRyb3BsZXZlbHMoUGxvdE9ic2VydmF0aW9uSUQpKSAlPiUNCiAgcHVsbChQbG90T2JzZXJ2YXRpb25JRCkNCkdBTV9kYXRhX2lkc19xMCA8LSBHQU1fZGF0YSAlPiUNCiAgIyBKb2luIHRvIGdldCBiaW9nZW8gYW5kIHVuaXQNCiAgbGVmdF9qb2luKGRhdGFfUlNfTGFuZHNhdF9iYW5kc19pbmRpY2VzICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIGJpb2dlbywgdW5pdCkgJT4lDQogICAgICAgICAgICAgIGRpc3RpbmN0KCkpICU+JQ0KICAjIEpvaW4gdG8gZ2V0IEVVTklTIGluZm8NCiAgIGxlZnRfam9pbihkYl9FdXJvcGFfYWxsb2JzICU+JQ0KICAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBFVU5JU2FfMSwgRVVOSVNhXzFfZGVzY3IsDQogICAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIsIEVVTklTYV8yX2Rlc2NyKSkgJT4lDQogICMgSm9pbiB0byBnZXQgb3JpZ2luYWwgdmFsdWVzIG9mIGluZGljZXMNCiAgbGVmdF9qb2luKGRhdGFfUlNfTGFuZHNhdF9iYW5kc19pbmRpY2VzICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIERPWSwgTkRWSSwgRVZJLCBTQVZJKSAlPiUNCiAgICAgICAgICAgICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKE5EVkksIEVWSSwgU0FWSSksIG5hbWVzX3RvID0gImluZGV4IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWVfb3JpZyIpKSAlPiUNCiAgIyBKb2luIHRvIGdldCB0c19xdWFsaXR5IGRhdGENCiAgbGVmdF9qb2luKHRzX3F1YWxpdHlfZmxhZyAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBuX21vbnRocywgcXVhbGl0eV9mbGFnKSkgJT4lDQogICMgS2VlcCBvbmx5IHRob3NlIHdpdGggcXVhbGl0eV9mbGFnID09IDANCiAgZHBseXI6OmZpbHRlcihxdWFsaXR5X2ZsYWcgPT0gMCkNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBHZXQgdW5pcXVlIFBsb3RPYnNlcnZhdGlvbklEcw0KdW5pcXVlX2lkczAgPC0gaWRzX3EwDQoNCiMgQ3JlYXRlIGFuZCBzdG9yZSBwbG90cyBpbiBhIGxpc3QNCnRzX3Bsb3RzX3EwIDwtIG1hcCh1bmlxdWVfaWRzMCwgZnVuY3Rpb24oaWQpIHsNCiAgcGxvdF9kYXRhIDwtIEdBTV9kYXRhX2lkc19xMCAlPiUNCiAgICBtdXRhdGUoUGxvdE9ic2VydmF0aW9uSUQgPSBhcy5jaGFyYWN0ZXIoUGxvdE9ic2VydmF0aW9uSUQpKSAlPiUNCiAgICBkcGx5cjo6ZmlsdGVyKFBsb3RPYnNlcnZhdGlvbklEID09IGlkKSANCiAgDQogICMgRXh0cmFjdCBtZXRhZGF0YSBmb3IgdGl0bGUNCiAgbWV0YWRhdGEgPC0gcGxvdF9kYXRhICU+JQ0KICAgIHNlbGVjdChiaW9nZW8sIHVuaXQsIEVVTklTYV8xLCBFVU5JU2FfMiwgcXVhbGl0eV9mbGFnKSAlPiUNCiAgICBkaXN0aW5jdCgpDQogIA0KICBnZ3Bsb3QoKSArDQogICAgIyBSYXcgZGF0YSBwb2ludHMNCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwbG90X2RhdGEsYWVzKHggPSBET1ksIHkgPSB2YWx1ZV9vcmlnKSwgYWxwaGEgPSAwLjUpICsNCiAgICBnZW9tX2xpbmUoZGF0YSA9IHBsb3RfZGF0YSwgYWVzKHggPSBET1ksIHkgPSB2YWx1ZSksIA0KICAgICAgICAgICAgICBzaXplID0gMC41LCBjb2xvciA9ICJibHVlIikgKw0KICAgIGdlb21fdmxpbmUoZGF0YSA9IHBsb3RfZGF0YSAlPiUgZGlzdGluY3QoaW5kZXgsIHNvc19zbG9wZSksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IHNvc19zbG9wZSwgZ3JvdXAgPSBpbmRleCksDQogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMC41LCBjb2xvciA9ICJyZWQiKSArDQogICAgZ2VvbV92bGluZShkYXRhID0gcGxvdF9kYXRhICU+JSBkaXN0aW5jdChpbmRleCwgc29zX3RocmVzaG9sZCksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IHNvc190aHJlc2hvbGQsIGdyb3VwID0gaW5kZXgpLA0KICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDAuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICAgIGdlb21fdmxpbmUoZGF0YSA9IHBsb3RfZGF0YSAlPiUgZGlzdGluY3QoaW5kZXgsIHBvcyksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IHBvcywgZ3JvdXAgPSBpbmRleCksDQogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkb3R0ZWQiLCBzaXplID0gMC41LCBjb2xvciA9ICJibHVlIikgKw0KICAgIGdlb21fdmxpbmUoZGF0YSA9IHBsb3RfZGF0YSAlPiUgZGlzdGluY3QoaW5kZXgsIGVvc19zbG9wZSksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IGVvc19zbG9wZSwgZ3JvdXAgPSBpbmRleCksDQogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMC41LCBjb2xvciA9ICJyZWQiKSArDQogICAgZ2VvbV92bGluZShkYXRhID0gcGxvdF9kYXRhICU+JSBkaXN0aW5jdChpbmRleCwgZW9zX3RocmVzaG9sZCksDQogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IGVvc190aHJlc2hvbGQsIGdyb3VwID0gaW5kZXgpLA0KICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDAuNSwgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICAgIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoaW5kZXgpKSArDQogICAgbGFicygNCiAgICAgIHRpdGxlID0gZ2x1ZTo6Z2x1ZSgie2lkfSB8IHttZXRhZGF0YSRiaW9nZW99e21ldGFkYXRhJHVuaXR9IHwge21ldGFkYXRhJEVVTklTYV8xfSB8IHttZXRhZGF0YSRFVU5JU2FfMn0gfCBRdWFsaXR5OiB7bWV0YWRhdGEkcXVhbGl0eV9mbGFnfSIpLA0KICAgICAgeCA9ICJEYXkgb2YgWWVhciIsDQogICAgICB5ID0gIkluZGV4IFZhbHVlIg0KICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KfSkNCg0KIyBOYW1lIHRoZSBsaXN0IGJ5IFBsb3RPYnNlcnZhdGlvbklEDQpuYW1lcyh0c19wbG90c19xMCkgPC0gdW5pcXVlX2lkczANCg0KIyBEaXNwbGF5IHRoZSBmaXJzdCBwbG90DQp0c19wbG90c19xMFsxXQ0KYGBgDQoNClNhdmUgZWFjaCBwbG90IHRvIGEgZmlsZToNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCndhbGsyKHRzX3Bsb3RzX3EwLCBzZXFfYWxvbmcodHNfcGxvdHNfcTApLCB+IGdnc2F2ZSgNCiAgZmlsZW5hbWUgPSBwYXN0ZTAoIkM6L0FuYWx5c2VzL01PVElWQVRFX3ZhbGlkYXRpb24vb3V0cHV0L2ZpZ3VyZXMvcGhlbm9sb2d5L3RzX3EwX0xhbmRzYXQvdHNfcGxvdHNfcTAiLCAueSwgIi5qcGVnIiksDQogIHBsb3QgPSAueCwNCiAgd2lkdGggPSA4LA0KICBoZWlnaHQgPSA1DQopKQ0KYGBgDQoNCiMgU21vb3RoIHRoZSB0aW1lIHNlcmllcyBvZiBORE1JIGFuZCBORFdJDQoNCiMjIEZ1bmN0aW9uDQoNClVzaW5nIEdBTSwgd2l0aG91dCByZXBsYWNpbmcgdmFsdWVzIGluIERPWSAx4oCTNTAgYW5kIERPWSAzMTXigJNlbmQgd2l0aCBzZXBhcmF0ZSBiYXNlIHZhbHVlcywgbGF0ZXIgdXNlIG9ubHkgdW53ZWlnaHRlZCBHQU0uDQoNCmBgYHtyfQ0KY29tcHV0ZV91bndlaWdodGVkX2ZpdCA8LSBmdW5jdGlvbigNCiAgICAjIERhdGEgZnJhbWUgZGYgd2l0aCBpbmRleCB2YWx1ZXMgb3ZlciB0aW1lIChET1kpDQogICAgZGYsIA0KICAgICMgTmFtZSBvZiB0aGUgdmVnZXRhdGlvbiBpbmRpY2VzIGNvbHVtbnMgKGUuZy4sICJORFZJIiwgIkVWSSIsICJTQVZJKQ0KICAgIGluZGV4X2NvbHMgPSBjKCJORE1JIiwgIk5EV0kiKQ0KKSB7DQogICMgSW5pdGlhbGl6ZSBsaXN0IHRvIHN0b3JlIHJlc3VsdHMNCiAgZml0c19saXN0IDwtIGxpc3QoKQ0KICANCiAgIyBMb29wIG92ZXIgZWFjaCBpbmRleCBjb2x1bW4NCiAgZm9yIChpbmRleF9jb2wgaW4gaW5kZXhfY29scykgew0KICAgIGRmX2luZGV4IDwtIGRmICU+JQ0KICAgICAgIyBSZW1vdmUgcm93cyB3aXRoIG1pc3NpbmcgaW5kZXggdmFsdWVzIGFuZCBzb3J0IGRhdGEgYnkgRE9ZDQogICAgICBmaWx0ZXIoIWlzLm5hKC5kYXRhW1tpbmRleF9jb2xdXSkpICU+JSBhcnJhbmdlKERPWSkNCiAgICANCiAgICAjIEV4dHJhY3QgeCAoRE9ZKSBhbmQgeSAoaW5kZXgpIHZlY3RvcnMgZm9yIG1vZGVsbGluZw0KICAgIHggPC0gZGZfaW5kZXgkRE9ZDQogICAgeSA8LSBkZl9pbmRleFtbaW5kZXhfY29sXV0NCiAgICANCiAgICAjIElmIHRoZXJlIGFyZSBmZXdlciB0aGFuIDExIG9ic2VydmF0aW9ucyBvciBhbGwgdmFsdWVzIGFyZSBOQSwgc2tpcA0KICAgIGlmIChsZW5ndGgoeCkgPCAxMSB8fCBhbGwoaXMubmEoeSkpKSB7DQogICAgICBuZXh0DQogICAgfQ0KICAgIA0KICAgICMgRml0IEdBTSAodW53ZWlnaHRlZCkgd2l0aCBhIHRoaW4gcGxhdGUgc3BsaW5lIChicyA9ICJ0cCIpDQogICAgIyB0byBzbW9vdGggdGhlIGluZGV4IGN1cnZlDQogICAgZ2FtX3Vud2VpZ2h0ZWQgPC0gbWdjdjo6YmFtKHkgfiBzKHgsIGJzID0gInRwIikpDQogICAgcHJlZCA8LSBwcmVkaWN0KGdhbV91bndlaWdodGVkLCBuZXdkYXRhID0gdGliYmxlKHggPSB4KSkNCiAgICANCiAgICAjIENyZWF0ZSB0aWJibGUgdG8gc3RvcmUgb3JpZ2luYWwgYW5kIHByZWRpY3RlZCBpbmRleCB2YWx1ZXMNCiAgICBmaXRzX2RmIDwtIHRpYmJsZSgNCiAgICAgIFBsb3RPYnNlcnZhdGlvbklEID0gdW5pcXVlKGRmJFBsb3RPYnNlcnZhdGlvbklEKSwNCiAgICAgIERPWSA9IHgsDQogICAgICBpbmRleCA9IGluZGV4X2NvbCwNCiAgICAgIHZhbHVlID0gcHJlZA0KICAgICkNCiAgICANCiAgICBmaXRzX2xpc3RbW2luZGV4X2NvbF1dIDwtIGZpdHNfZGYNCiAgfQ0KICANCiAgaWYgKGxlbmd0aChmaXRzX2xpc3QpID09IDApIHsNCiAgICByZXR1cm4odGliYmxlKCkpDQogIH0NCiAgDQogIGJpbmRfcm93cyhmaXRzX2xpc3QpDQp9DQpgYGANCg0KIyMgQ2FsY3VsYXRpb24NCg0KQXBwbHkgdGhlIGZ1bmN0aW9uIHdpdGggYmF0Y2ggcHJvY2Vzc2luZy4NCg0KYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnBsYW4obXVsdGlzZXNzaW9uLCB3b3JrZXJzID0gMjQpDQoNCmlkcyA8LSB1bmlxdWUoZGF0YV9SU19MYW5kc2F0X2JhbmRzX2luZGljZXMkUGxvdE9ic2VydmF0aW9uSUQpDQpiYXRjaGVzIDwtIHNwbGl0KGlkcywgY2VpbGluZyhzZXFfYWxvbmcoaWRzKSAvIDUwKSkgICMgYmF0Y2hlcyBvZiA1MA0KDQpzdGFydF90b3RhbCA8LSBTeXMudGltZSgpDQoNCnNtb290aGVkX2RhdGEgPC0gbWFwX2RmcihzZXFfYWxvbmcoYmF0Y2hlcyksIGZ1bmN0aW9uKGkpIHsNCiAgYmF0Y2hfaWRzIDwtIGJhdGNoZXNbW2ldXQ0KICB0b3RhbF9iYXRjaGVzIDwtIGxlbmd0aChiYXRjaGVzKQ0KICBiYXRjaF9maWxlIDwtIGZpbGUucGF0aCgib2JqZWN0cy9zbW9vdGhpbmdfYmF0Y2hlc19MYW5kc2F0IiwgcGFzdGUwKCJiYXRjaF8iLCBpLCAiLnJkcyIpKQ0KDQogIGlmIChmaWxlLmV4aXN0cyhiYXRjaF9maWxlKSkgew0KICAgIG1lc3NhZ2UoIuKchSBCYXRjaCAgIiwgaSwgIiBvZiAiLCB0b3RhbF9iYXRjaGVzLCANCiAgICAgICAgICAgICIgYWxyZWFkeSBwcm9jZXNzZWQuIExvYWRpbmcgZnJvbSBmaWxlLiIpDQogICAgcmV0dXJuKHJlYWRSRFMoYmF0Y2hfZmlsZSkpDQogIH0NCg0KICBtZXNzYWdlKCLwn5SEIFByb2Nlc3NpbmcgYmF0Y2ggICIsIGksICIgb2YgIiwgdG90YWxfYmF0Y2hlcywgIiB3aXRoICIsDQogICAgICAgICAgbGVuZ3RoKGJhdGNoX2lkcyksICIgSURzLi4uIikNCg0KICBzdGFydF9iYXRjaCA8LSBTeXMudGltZSgpDQoNCiAgcmVzdWx0IDwtIGRhdGFfUlNfTGFuZHNhdF9iYW5kc19pbmRpY2VzICU+JQ0KICAgIGZpbHRlcihQbG90T2JzZXJ2YXRpb25JRCAlaW4lIGJhdGNoX2lkcykgJT4lDQogICAgZ3JvdXBfc3BsaXQoUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICAgIHNldF9uYW1lcyhtYXBfY2hyKC4sIH4gYXMuY2hhcmFjdGVyKHVuaXF1ZSgueCRQbG90T2JzZXJ2YXRpb25JRCkpKSkgJT4lDQogICAgZnV0dXJlX21hcF9kZnIofiBjb21wdXRlX3Vud2VpZ2h0ZWRfZml0KGRmID0gLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXhfY29scyA9IGMoIk5ETUkiLCAiTkRXSSIpKSwNCiAgICAgICAgICAgICAgICAgICAucHJvZ3Jlc3MgPSBUUlVFKQ0KDQogIGVuZF9iYXRjaCA8LSBTeXMudGltZSgpDQogIGR1cmF0aW9uIDwtIHJvdW5kKGRpZmZ0aW1lKGVuZF9iYXRjaCwgc3RhcnRfYmF0Y2gsIHVuaXRzID0gIm1pbnMiKSwgMikNCiAgbWVzc2FnZSgi4o+x77iPIEJhdGNoIHRpbWUgIiwgaSwgIjogIiwgZHVyYXRpb24sICIgbWludXRlcyIpDQoNCiAgbWVzc2FnZSgi8J+SviBTYXZpbmcgYmF0Y2ggIiwgaSwgIiB0byBmaWxlLi4uIikNCiAgc2F2ZVJEUyhyZXN1bHQsIGJhdGNoX2ZpbGUpDQogIG1lc3NhZ2UoIuKchSBCYXRjaCAiLCBpLCAiIHNhdmVkLiIpIA0KDQogIHJlc3VsdA0KfSkNCg0KZW5kX3RvdGFsIDwtIFN5cy50aW1lKCkNCnRvdGFsX3RpbWUgPC0gcm91bmQoZGlmZnRpbWUoZW5kX3RvdGFsLCBzdGFydF90b3RhbCwgdW5pdHMgPSAibWlucyIpLCAyKQ0KbWVzc2FnZSgi4o+x77iPIFRvdGFsIHRpbWU6ICIsIHRvdGFsX3RpbWUsICIgbWludXRlcyIpDQpgYGANCg0KYGBge3J9DQpwbGFuKHNlcXVlbnRpYWwpDQpgYGANCg0KIyMgU2F2ZQ0KDQpMb29rOg0KDQpgYGB7cn0NCnNtb290aGVkX2RhdGENCmBgYA0KDQpTYXZlIGFzIGFuIG9iamVjdDoNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnNhdmUoc21vb3RoZWRfZGF0YSwgZmlsZSA9ICJvYmplY3RzL3Ntb290aGVkX2RhdGFfTGFuZHNhdC5SZGF0YSIpDQpgYGANCg0KIyMgVEJEIGlmIG5lZWRlZDogUGxvdCBmaXQgYW5kIG1vbWVudHMgZm9yIGVhY2ggUGxvdE9ic2VydmF0aW9uSUQNCg0KYGBge3J9DQpzbW9vdGhlZF9kYXRhX2lkcyA8LSBzbW9vdGhlZF9kYXRhICU+JQ0KICAjIEpvaW4gdG8gZ2V0IGJpb2dlbyBhbmQgdW5pdA0KICBsZWZ0X2pvaW4oZGF0YV9SU19MYW5kc2F0X2JhbmRzX2luZGljZXMgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgYmlvZ2VvLCB1bml0KSAlPiUNCiAgICAgICAgICAgICAgZGlzdGluY3QoKSkgJT4lDQogICMgSm9pbiB0byBnZXQgRVVOSVMgaW5mbw0KICAgbGVmdF9qb2luKGRiX0V1cm9wYV9hbGxvYnMgJT4lDQogICAgICAgICAgICAgICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIEVVTklTYV8xLCBFVU5JU2FfMV9kZXNjciwNCiAgICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiwgRVVOSVNhXzJfZGVzY3IpKSAlPiUNCiAgbXV0YXRlKFBsb3RPYnNlcnZhdGlvbklEID0gYXMuY2hhcmFjdGVyKFBsb3RPYnNlcnZhdGlvbklEKSkNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyBHZXQgdW5pcXVlIFBsb3RPYnNlcnZhdGlvbklEcw0KdW5pcXVlX2lkcyA8LSB1bmlxdWUoc21vb3RoZWRfZGF0YV9pZHMkUGxvdE9ic2VydmF0aW9uSUQpDQoNCiMgQ3JlYXRlIGFuZCBzdG9yZSBwbG90cyBpbiBhIGxpc3QNCnRzX3Bsb3RzX05ETUlfTkRXSTwtIG1hcCh1bmlxdWVfaWRzLCBmdW5jdGlvbihpZCkgew0KICBwbG90X2RhdGEgPC0gc21vb3RoZWRfZGF0YV9pZHMgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoUGxvdE9ic2VydmF0aW9uSUQgPT0gaWQpDQogIA0KICAjIEV4dHJhY3QgbWV0YWRhdGEgZm9yIHRpdGxlDQogIG1ldGFkYXRhIDwtIHBsb3RfZGF0YSAlPiUNCiAgICBzZWxlY3QoYmlvZ2VvLCB1bml0LCBFVU5JU2FfMSwgRVVOSVNhXzIpICU+JQ0KICAgIGRpc3RpbmN0KCkNCiAgDQogIGdncGxvdCgpICsNCiAgICAjIFJhdyBkYXRhIHBvaW50cw0KICAgICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcyAlPiUNCiAgICAgICAgICAgICAgICAgIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgRE9ZLCBORE1JLCBORFdJKSAlPiUNCiAgICAgICAgICAgICAgICAgIHBpdm90X2xvbmdlcihjb2xzID0gYyhORE1JLCBORFdJKSwgbmFtZXNfdG8gPSAiaW5kZXgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQ0KICAgICAgICAgICAgICAgICAgZmlsdGVyKFBsb3RPYnNlcnZhdGlvbklEID09IGlkKSwNCiAgICAgICAgICAgICAgICBhZXMoeCA9IERPWSwgeSA9IHZhbHVlKSwgYWxwaGEgPSAwLjYpICsNCiAgICBnZW9tX2xpbmUoZGF0YSA9IHBsb3RfZGF0YSwgYWVzKHggPSBET1ksIHkgPSB2YWx1ZSksDQogICAgICAgICAgICAgIHNpemUgPSAwLjUsIGNvbG9yID0gImJsdWUiKSArDQogICAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhpbmRleCkpICsNCiAgICBsYWJzKA0KICAgICAgdGl0bGUgPSBnbHVlOjpnbHVlKCJ7aWR9IHwge21ldGFkYXRhJGJpb2dlb317bWV0YWRhdGEkdW5pdH0gfCB7bWV0YWRhdGEkRVVOSVNhXzF9IHwge21ldGFkYXRhJEVVTklTYV8yfSIpLA0KICAgICAgeCA9ICJEYXkgb2YgWWVhciIsDQogICAgICB5ID0gIkluZGV4IFZhbHVlIg0KICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KfSkNCg0KIyBOYW1lIHRoZSBsaXN0IGJ5IFBsb3RPYnNlcnZhdGlvbklEDQpuYW1lcyh0c19wbG90c19ORE1JX05EV0kpIDwtIHVuaXF1ZV9pZHMNCg0KIyBEaXNwbGF5IHRoZSBmaXJzdCBwbG90DQpwcmludCh0c19wbG90c19ORE1JX05EV0lbWzFdXSkNCmBgYA0KDQpTYXZlIGVhY2ggcGxvdCB0byBhIGZpbGU6DQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQp3YWxrMih0c19wbG90c19ORE1JX05EV0ksIHNlcV9hbG9uZyh0c19wbG90c19ORE1JX05EV0kpLCB+IGdnc2F2ZSgNCiAgZmlsZW5hbWUgPSBwYXN0ZTAoIkM6L0FuYWx5c2VzL01PVElWQVRFX3ZhbGlkYXRpb24vb3V0cHV0L2ZpZ3VyZXMvcGhlbm9sb2d5L3RzX05ETUlfTkRXSV9MYW5kc2F0L3RzX3Bsb3RzX05EVklfTkRNSSIsDQogICAgICAgICAgICAgICAgICAgIC55LCAiLmpwZWciKSwNCiAgcGxvdCA9IC54LA0KICB3aWR0aCA9IDgsDQogIGhlaWdodCA9IDUNCikpDQpgYGANCg0KIyBHZXQgaW5kaWNlcyBkYXRhIChtYXguIGFuZCBtaW4uKQ0KDQpDYXJlZnVsISBUaGVzZSBtYXhpbXVtIGFuZCBtaW5pbXVtIHZhbHVlcyBhcmUgZnJvbSB0aGUgc21vb3RoZWQgdGltZSBzZXJpZXMuIEZvciBORFZJIC8gRVZJIC8gU0FWSSB2YWx1ZXMgaW4gRE9ZIDHigJM1MCBhbmQgRE9ZIDMxNeKAk2VuZCwgcmVtZW1iZXIgdGhhdCB0aGUgR0FNIHNtb290aGluZyBmdW5jdGlvbiByZXBsYWNlZCB0aGUgb3JpZ2luYWwgdmFsdWVzIHdpdGggdGhlIG1lYW4gYmFzZSB2YWx1ZSBvZiBvYnNlcnZhdGlvbnMgZHVyaW5nIGVhY2ggb2YgdGhlc2UgcmVzcGVjdGl2ZSBwZXJpb2RzLiBUaGlzIHdhcyBzbyBmYXIgbm90IGRvbmUgZm9yIE5ETUkgYW5kIE5EV0kuIA0KDQpgYGB7cn0NCmZpbmFsX2luZGljZXNfZGF0YSA8LSBHQU1fZGF0YSAlPiUNCiAgZ3JvdXBfYnkoUGxvdE9ic2VydmF0aW9uSUQsIGluZGV4KSAlPiUNCiAgc3VtbWFyaXNlKG1heCA9IG1heCh2YWx1ZSksIG1pbiA9IG1pbih2YWx1ZSkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBpbmRleCwgdmFsdWVzX2Zyb20gPSBjKG1heCwgbWluKSwNCiAgICAgICAgICAgICAgbmFtZXNfZ2x1ZSA9ICJ7aW5kZXh9X3sudmFsdWV9IikgJT4lDQogIGZ1bGxfam9pbigNCiAgICBzbW9vdGhlZF9kYXRhICU+JQ0KICAgICAgZ3JvdXBfYnkoUGxvdE9ic2VydmF0aW9uSUQsIGluZGV4KSAlPiUNCiAgICAgIHN1bW1hcmlzZShtYXggPSBtYXgodmFsdWUpLCBtaW4gPSBtaW4odmFsdWUpKSAlPiUNCiAgICAgIHVuZ3JvdXAoKSAlPiUNCiAgICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBpbmRleCwgdmFsdWVzX2Zyb20gPSBjKG1heCwgbWluKSwNCiAgICAgICAgICAgICAgICAgIG5hbWVzX2dsdWUgPSAie2luZGV4fV97LnZhbHVlfSIpDQogICAgKQ0KYGBgDQoNCiMgR2V0IHBoZW5vbG9neSBkYXRhDQoNClVzZSBHQU0gaXRlcl8zIHRvIGdldCBkYXRlcyBvZiB0aGUgbW9tZW50cywgdmFsdWVzIGF0IHRob3NlIG1vbWVudHMgYW5kIEFVQyAodGltZS1pbnRlZ3JhdGVkIGluZGljZXMpIGJldHdlZW4gU09TIGFuZCBFT1M6DQoNCmBgYHtyfQ0KIyBKb2luIHRvIGdldCB2YWx1ZXMgYXQgU09TLCBQT1MsIEVPUyBhbmQgYXVjDQpmaW5hbF9waGVub2xvZ3lfZGF0YSA8LSBHQU1fZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIHN0YWdlID0gY2FzZV93aGVuKA0KICAgICAgRE9ZID09IHNvc19zbG9wZSB+ICJzb3Nfc2xvcGUiLA0KICAgICAgRE9ZID09IHNvc190aHJlc2hvbGQgfiAic29zX3RocmVzaG9sZCIsDQogICAgICBET1kgPT0gcG9zIH4gInBvcyIsDQogICAgICBET1kgPT0gZW9zX3Nsb3BlIH4gImVvc19zbG9wZSIsDQogICAgICBET1kgPT0gZW9zX3RocmVzaG9sZCB+ICJlb3NfdGhyZXNob2xkIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKQ0KICApICU+JQ0KICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShzdGFnZSkpICU+JQ0KICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIGluZGV4LCBzdGFnZSwgZG95ID0gRE9ZLCB2YWx1ZSkgJT4lDQogIHBpdm90X3dpZGVyKA0KICAgIG5hbWVzX2Zyb20gPSBjKGluZGV4LCBzdGFnZSksDQogICAgdmFsdWVzX2Zyb20gPSBjKGRveSwgdmFsdWUpLA0KICAgIG5hbWVzX2dsdWUgPSAie2luZGV4fV97c3RhZ2V9X3sudmFsdWV9Ig0KICApICU+JQ0KICAjIENvbnZlcnQgbGlzdCBjb2xzIHRvIHJlZ3VsYXIgbnVtZXJpYyBjb2xzDQogIG11dGF0ZSgNCiAgICBORFZJX3Nvc19zbG9wZV92YWx1ZSA9IG1hcF9kYmwoTkRWSV9zb3Nfc2xvcGVfdmFsdWUsIDEpLA0KICAgIE5EVklfc29zX3RocmVzaG9sZF92YWx1ZSA9IG1hcF9kYmwoTkRWSV9zb3NfdGhyZXNob2xkX3ZhbHVlLCAxKSwNCiAgICBORFZJX3Bvc192YWx1ZSA9IG1hcF9kYmwoTkRWSV9wb3NfdmFsdWUsIDEpLA0KICAgIE5EVklfZW9zX3Nsb3BlX3ZhbHVlID0gbWFwX2RibChORFZJX2Vvc19zbG9wZV92YWx1ZSwgMSksDQogICAgTkRWSV9lb3NfdGhyZXNob2xkX3ZhbHVlID0gbWFwX2RibChORFZJX2Vvc190aHJlc2hvbGRfdmFsdWUsIDEpLA0KICAgIEVWSV9zb3Nfc2xvcGVfdmFsdWUgPSBtYXBfZGJsKEVWSV9zb3Nfc2xvcGVfdmFsdWUsIDEpLA0KICAgIEVWSV9zb3NfdGhyZXNob2xkX3ZhbHVlID0gbWFwX2RibChFVklfc29zX3RocmVzaG9sZF92YWx1ZSwgMSksDQogICAgRVZJX3Bvc192YWx1ZSA9IG1hcF9kYmwoRVZJX3Bvc192YWx1ZSwgMSksDQogICAgRVZJX2Vvc19zbG9wZV92YWx1ZSA9IG1hcF9kYmwoRVZJX2Vvc19zbG9wZV92YWx1ZSwgMSksDQogICAgRVZJX2Vvc190aHJlc2hvbGRfdmFsdWUgPSBtYXBfZGJsKEVWSV9lb3NfdGhyZXNob2xkX3ZhbHVlLCAxKSwNCiAgICBTQVZJX3Nvc19zbG9wZV92YWx1ZSA9IG1hcF9kYmwoU0FWSV9zb3Nfc2xvcGVfdmFsdWUsIDEpLA0KICAgIFNBVklfc29zX3RocmVzaG9sZF92YWx1ZSA9IG1hcF9kYmwoU0FWSV9zb3NfdGhyZXNob2xkX3ZhbHVlLCAxKSwNCiAgICBTQVZJX3Bvc192YWx1ZSA9IG1hcF9kYmwoU0FWSV9wb3NfdmFsdWUsIDEpLA0KICAgIFNBVklfZW9zX3Nsb3BlX3ZhbHVlID0gbWFwX2RibChTQVZJX2Vvc19zbG9wZV92YWx1ZSwgMSksDQogICAgU0FWSV9lb3NfdGhyZXNob2xkX3ZhbHVlID0gbWFwX2RibChTQVZJX2Vvc190aHJlc2hvbGRfdmFsdWUsIDEpDQogICkgJT4lDQogIGZ1bGxfam9pbihHQU1fZGF0YSAlPiUNCiAgICAgICAgICAgICAgZGlzdGluY3QoUGxvdE9ic2VydmF0aW9uSUQsIGluZGV4LCBhdWNfc2xvcGUsIGF1Y190aHJlc2hvbGQpICU+JQ0KICAgICAgICAgICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaW5kZXgsIHZhbHVlc19mcm9tID0gYyhhdWNfc2xvcGUsIGF1Y190aHJlc2hvbGQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc19nbHVlID0gIntpbmRleH1fey52YWx1ZX0iKSkNCmBgYA0KDQojIEpvaW4gaW5kaWNlcyBhbmQgcGhlbm9sb2d5IGRhdGENCg0KYGBge3J9DQpmaW5hbF9SU19kYXRhIDwtIGZ1bGxfam9pbigNCiAgIyBJbmRpY2VzIGRhdGEgKG1heCBhbmQgbWluKQ0KICBmaW5hbF9pbmRpY2VzX2RhdGEsDQogICMgQXZlcmFnZSB2YWx1ZXMgb2YgaW5kaWNlcyBwZXIgbW9udGgNCiAgbW9udGhseV9hdmdfaW5kaWNlcyAlPiUNCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaW5kZXgsIHZhbHVlc19mcm9tID0gYyhhdmdfdmFsdWVfMDE6YXZnX3ZhbHVlXzEyKSwNCiAgICAgICAgICAgICAgICBuYW1lc19nbHVlID0gIntpbmRleH1fey52YWx1ZX0iKQ0KICApICU+JQ0KICBmdWxsX2pvaW4oDQogICAgIyBQaGVub2xvZ3kgZGF0YQ0KICAgIGZpbmFsX3BoZW5vbG9neV9kYXRhIA0KICAgICkgJT4lDQogICMgU29ydCBjb2xzIGluIGFscGhhYmV0aWNhbCBvcmRlcg0KICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIHNvcnQobmFtZXMoLilbbmFtZXMoLikgIT0gIlBsb3RPYnNlcnZhdGlvbklEIl0pKQ0KYGBgDQoNCiMgQWRkIEVVTklTIGNvZGVzDQoNCmBgYHtyfQ0KZmluYWxfUlNfZGF0YSA8LSBmaW5hbF9SU19kYXRhICU+JSBsZWZ0X2pvaW4oZGJfRXVyb3BhX2FsbG9icykNCmBgYA0KDQpgYGB7cn0NCmRhdGFfUlNfTGFuZHNhdF9iYW5kc19pbmRpY2VzIDwtIGRhdGFfUlNfTGFuZHNhdF9iYW5kc19pbmRpY2VzICU+JQ0KICBsZWZ0X2pvaW4oZGJfRXVyb3BhX2FsbG9icykNCmBgYA0KDQojIE1vbnRobHkgc3BlY3Ryb3BoZW5vbG9neSBwZXIgaGFiaXRhdCB0eXBlDQoNCmBgYHtyfQ0KIyBQcmVwYXJlIHRoZSBkYXRhDQpkYXRhX21vbnRobHlfRVVOSVNhXzEgPC0gZGF0YV9SU19MYW5kc2F0X2JhbmRzX2luZGljZXMgJT4lDQogIG11dGF0ZShtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoZGF0ZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhbGU9IkVOLXVzIikpICU+JQ0KICBncm91cF9ieShtb250aCwgRVVOSVNhXzEsIEVVTklTYV8xX2Rlc2NyKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG1lYW5fTkRWSSA9IG1lYW4oTkRWSSwgbmEucm0gPSBUUlVFKSwNCiAgICBzZF9ORFZJID0gc2QoTkRWSSwgbmEucm0gPSBUUlVFKSwNCiAgICBuX05EVkkgPSBzdW0oIWlzLm5hKE5EVkkpKSwNCiAgICBtZWFuX0VWSSA9IG1lYW4oRVZJLCBuYS5ybSA9IFRSVUUpLA0KICAgIHNkX0VWSSA9IHNkKEVWSSwgbmEucm0gPSBUUlVFKSwNCiAgICBuX0VWSSA9IHN1bSghaXMubmEoRVZJKSksDQogICAgbWVhbl9TQVZJID0gbWVhbihTQVZJLCBuYS5ybSA9IFRSVUUpLA0KICAgIHNkX1NBVkkgPSBzZChTQVZJLCBuYS5ybSA9IFRSVUUpLA0KICAgIG5fU0FWSSA9IHN1bSghaXMubmEoU0FWSSkpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKQ0KDQpkYXRhX21vbnRobHlfRVVOSVNhXzEgPC0gZGF0YV9tb250aGx5X0VVTklTYV8xICU+JQ0KICAjIEFkZCBsYWJlbCB3aXRoIG4NCiAgbGVmdF9qb2luKGRhdGFfbW9udGhseV9FVU5JU2FfMSAlPiUNCiAgICAgICAgICAgICAgZ3JvdXBfYnkoRVVOSVNhXzEpICU+JQ0KICAgICAgICAgICAgICBzdW1tYXJpc2Uobl90b3RhbCA9IHN1bShuX05EVkksIG5hLnJtID0gVFJVRSkpLCANCiAgICAgICAgICAgIGJ5ID0gIkVVTklTYV8xIikgJT4lDQogIG11dGF0ZShFVU5JU2FfMV9sYWJlbCA9IHBhc3RlMChFVU5JU2FfMSwgIiAobiA9ICIsIG5fdG90YWwsICIpIikpDQoNCmRhdGFfbW9udGhseV9FVU5JU2FfMiA8LSBkYXRhX1JTX0xhbmRzYXRfYmFuZHNfaW5kaWNlcyAlPiUNCiAgbXV0YXRlKG1vbnRoID0gbHVicmlkYXRlOjptb250aChkYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2FsZT0iRU4tdXMiKSkgJT4lDQogIGdyb3VwX2J5KG1vbnRoLCBFVU5JU2FfMSwgRVVOSVNhXzFfZGVzY3IsIEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjcikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBtZWFuX05EVkkgPSBtZWFuKE5EVkksIG5hLnJtID0gVFJVRSksDQogICAgc2RfTkRWSSA9IHNkKE5EVkksIG5hLnJtID0gVFJVRSksDQogICAgbl9ORFZJID0gc3VtKCFpcy5uYShORFZJKSksDQogICAgbWVhbl9FVkkgPSBtZWFuKEVWSSwgbmEucm0gPSBUUlVFKSwNCiAgICBzZF9FVkkgPSBzZChFVkksIG5hLnJtID0gVFJVRSksDQogICAgbl9FVkkgPSBzdW0oIWlzLm5hKEVWSSkpLA0KICAgIG1lYW5fU0FWSSA9IG1lYW4oU0FWSSwgbmEucm0gPSBUUlVFKSwNCiAgICBzZF9TQVZJID0gc2QoU0FWSSwgbmEucm0gPSBUUlVFKSwNCiAgICBuX1NBVkkgPSBzdW0oIWlzLm5hKFNBVkkpKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkNCg0KZGF0YV9tb250aGx5X0VVTklTYV8yIDwtIGRhdGFfbW9udGhseV9FVU5JU2FfMiAlPiUNCiAgIyBBZGQgbGFiZWwgd2l0aCBuDQogIGxlZnRfam9pbihkYXRhX21vbnRobHlfRVVOSVNhXzIgJT4lDQogICAgICAgICAgICAgIGdyb3VwX2J5KEVVTklTYV8yKSAlPiUNCiAgICAgICAgICAgICAgc3VtbWFyaXNlKG5fdG90YWwgPSBzdW0obl9ORFZJLCBuYS5ybSA9IFRSVUUpKSwgDQogICAgICAgICAgICBieSA9ICJFVU5JU2FfMiIpICU+JQ0KICBtdXRhdGUoRVVOSVNhXzJfbGFiZWwgPSBwYXN0ZTAoRVVOSVNhXzIsICIgKG4gPSAiLCBuX3RvdGFsLCAiKSIpKQ0KYGBgDQoNCiMjIEVVTklTIGxldmVsIDENCg0KYGBge3J9DQojIFBsb3RzDQoNCiMgRVVOSVNhXzENCmdncGxvdChkYXRhX21vbnRobHlfRVVOSVNhXzEgJT4lDQogICAgICAgICAjIElmIHdlIHdhbnQgdG8gaGF2ZSBuIHBvaW50cw0KICAgICAgICAgIyBtdXRhdGUoRVVOSVMgPSBwYXN0ZShFVU5JU2FfMV9sYWJlbCwgRVVOSVNhXzFfZGVzY3IsIHNlcCA9ICIgLSAiKSksIA0KICAgICAgICAgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzEsIEVVTklTYV8xX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gbWVhbl9ORFZJLCBjb2xvciA9IEVVTklTLCANCiAgICAgICAgICAgZ3JvdXAgPSBFVU5JU2FfMV9sYWJlbCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IEVVTklTYV8xKSkgKw0KICAjZ2VvbShhZXMoeW1pbiA9IG1lYW5fTkRWSSAtIHNkX05EVkksIHltYXggPSBtZWFuX05EVkkgKyBzZF9ORFZJKSwgDQogICN3aWR0aCA9IDAuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1vbnRobHkgTkRWSSBieSBIYWJpdGF0IFR5cGUiLA0KICAgIHggPSAiTW9udGgiLA0KICAgIHkgPSAiTkRWSSIsDQogICAgY29sb3IgPSAiSGFiaXRhdCAoRVVOSVMxKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KZ2dzYXZlKA0KICBoZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsICJtb250aGx5X3NwZWN0cm9waGVub2xvZ3lfTGFuZHNhdCIsICJFVU5JUzFfTkRWSS5qcGVnIiksDQogIGRwaSA9IDMwMCwgd2lkdGggPSA2LCBoZWlnaHQgPSAyLjUpDQoNCmdncGxvdChkYXRhX21vbnRobHlfRVVOSVNhXzEgJT4lDQogICAgICAgICAjIElmIHdlIHdhbnQgdG8gaGF2ZSBuIHBvaW50cw0KICAgICAgICAgIyBtdXRhdGUoRVVOSVMgPSBwYXN0ZShFVU5JU2FfMV9sYWJlbCwgRVVOSVNhXzFfZGVzY3IsIHNlcCA9ICIgLSAiKSksIA0KICAgICAgICAgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzEsIEVVTklTYV8xX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gbWVhbl9FVkksIGNvbG9yID0gRVVOSVMsIA0KICAgICAgICAgICBncm91cCA9IEVVTklTYV8xX2xhYmVsKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoKSArDQogICNnZW9tKGFlcyh5bWluID0gbWVhbl9FVkkgLSBzZF9FVkksIHltYXggPSBtZWFuX0VWSSArIHNkX0VWSSksIA0KICAgICAgICAgICAgICAgICN3aWR0aCA9IDAuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1vbnRobHkgRVZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJFVkkiLA0KICAgIGNvbG9yID0gIkhhYml0YXQgKEVVTklTMSkiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmdnc2F2ZSgNCiAgaGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCAibW9udGhseV9zcGVjdHJvcGhlbm9sb2d5X0xhbmRzYXQiLCAiRVVOSVMxX0VWSS5qcGVnIiksDQogIGRwaSA9IDMwMCwgd2lkdGggPSA2LCBoZWlnaHQgPSAyLjUpDQoNCmdncGxvdChkYXRhX21vbnRobHlfRVVOSVNhXzEgJT4lDQogICAgICAgICAjIElmIHdlIHdhbnQgdG8gaGF2ZSBuIHBvaW50cw0KICAgICAgICAgIyBtdXRhdGUoRVVOSVMgPSBwYXN0ZShFVU5JU2FfMV9sYWJlbCwgRVVOSVNhXzFfZGVzY3IsIHNlcCA9ICIgLSAiKSksIA0KICAgICAgICAgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzEsIEVVTklTYV8xX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gbWVhbl9TQVZJLCBjb2xvciA9IEVVTklTLCANCiAgICAgICAgICAgZ3JvdXAgPSBFVU5JU2FfMV9sYWJlbCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAjZ2VvbShhZXMoeW1pbiA9IG1lYW5fU0FWSSAtIHNkX1NBVkksIHltYXggPSBtZWFuX1NBVkkgKyBzZF9TQVZJKSwgDQogICAgICAgICAgICAgICAgI3dpZHRoID0gMC4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9udGhseSBTQVZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJTQVZJIiwNCiAgICBjb2xvciA9ICJIYWJpdGF0IChFVU5JUzEpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpnZ3NhdmUoDQogIGhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwgIm1vbnRobHlfc3BlY3Ryb3BoZW5vbG9neV9MYW5kc2F0IiwgIkVVTklTMV9TQVZJLmpwZWciKSwNCiAgZHBpID0gMzAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkNCmBgYA0KDQojIyBFVU5JUyBsZXZlbCAyDQoNCiMjIyBRDQoNCmBgYHtyfQ0KIyBORFZJDQpnZ3Bsb3QoZGF0YV9tb250aGx5X0VVTklTYV8yICU+JSANCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgPT0gIlEiICYgIWlzLm5hKEVVTklTYV8yKSkgJT4lDQogICAgICAgICAjIFJlbW92ZSB0aG9zZSB3aXRoIEVVTklTIGxldmVsIDIgdGhhdCBkb2VzIG5vdCBtYXRjaCBjdXJyZW50IGNsYXNzaWYNCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzIgIT0gIlFhIiAmIEVVTklTYV8yICE9ICJRYiIpICU+JQ0KICAgICAgICAgIyBJZiB3ZSB3YW50IHRvIGhhdmUgbiBwb2ludHMNCiAgICAgICAgICMgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzJfbGFiZWwsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICAgIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgYWVzKHggPSBtb250aCwgeSA9IG1lYW5fTkRWSSwgY29sb3IgPSBFVU5JUywgZ3JvdXAgPSBFVU5JUykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAjZ2VvbShhZXMoeW1pbiA9IG1lYW5fTkRWSSAtIHNkX05EVkksIHltYXggPSBtZWFuX05EVkkgKyBzZF9ORFZJKSwgDQogICAgICAgICAgICAgICAgI3dpZHRoID0gMC4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9udGhseSBORFZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJORFZJIiwNCiAgICBjb2xvciA9ICJIYWJpdGF0IChFVU5JUzIpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpnZ3NhdmUoDQogIGhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwgIm1vbnRobHlfc3BlY3Ryb3BoZW5vbG9neV9MYW5kc2F0IiwgIkVVTklTMl9RX05EVkkuanBlZyIpLA0KICBkcGkgPSAzMDAsIHdpZHRoID0gNywgaGVpZ2h0ID0gMi41KQ0KDQojIEVWSQ0KZ2dwbG90KGRhdGFfbW9udGhseV9FVU5JU2FfMiAlPiUgDQogICAgICAgICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8xID09ICJRIiAmICFpcy5uYShFVU5JU2FfMikpICU+JQ0KICAgICAgICAgIyBSZW1vdmUgdGhvc2Ugd2l0aCBFVU5JUyBsZXZlbCAyIHRoYXQgZG9lcyBub3QgbWF0Y2ggY3VycmVudCBjbGFzc2lmDQogICAgICAgICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8yICE9ICJRYSIgJiBFVU5JU2FfMiAhPSAiUWIiKSAlPiUNCiAgICAgICAgICMgSWYgd2Ugd2FudCB0byBoYXZlIG4gcG9pbnRzDQogICAgICAgICAjIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yX2xhYmVsLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgICBtdXRhdGUoRVVOSVMgPSBwYXN0ZShFVU5JU2FfMiwgRVVOSVNhXzJfZGVzY3IsIHNlcCA9ICIgLSAiKSksIA0KICAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBtZWFuX0VWSSwgY29sb3IgPSBFVU5JUywgZ3JvdXAgPSBFVU5JUykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAjZ2VvbShhZXMoeW1pbiA9IG1lYW5fRVZJIC0gc2RfRVZJLCB5bWF4ID0gbWVhbl9FVkkgKyBzZF9FVkkpLCANCiAgICAgICAgICAgICAgICAjd2lkdGggPSAwLjIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNb250aGx5IEVWSSBieSBIYWJpdGF0IFR5cGUiLA0KICAgIHggPSAiTW9udGgiLA0KICAgIHkgPSAiRVZJIiwNCiAgICBjb2xvciA9ICJIYWJpdGF0IChFVU5JUzIpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpnZ3NhdmUoDQogIGhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwgIm1vbnRobHlfc3BlY3Ryb3BoZW5vbG9neV9MYW5kc2F0IiwgIkVVTklTMl9RX0VWSS5qcGVnIiksDQogIGRwaSA9IDMwMCwgd2lkdGggPSA3LCBoZWlnaHQgPSAyLjUpDQoNCiMgU0FWSQ0KZ2dwbG90KGRhdGFfbW9udGhseV9FVU5JU2FfMiAlPiUNCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgPT0gIlEiICYgIWlzLm5hKEVVTklTYV8yKSkgJT4lDQogICAgICAgICAjIFJlbW92ZSB0aG9zZSB3aXRoIEVVTklTIGxldmVsIDIgdGhhdCBkb2VzIG5vdCBtYXRjaCBjdXJyZW50IGNsYXNzaWYNCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzIgIT0gIlFhIiAmIEVVTklTYV8yICE9ICJRYiIpICU+JQ0KICAgICAgICAgIyBJZiB3ZSB3YW50IHRvIGhhdmUgbiBwb2ludHMNCiAgICAgICAgICMgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzJfbGFiZWwsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICAgIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgYWVzKHggPSBtb250aCwgeSA9IG1lYW5fU0FWSSwgY29sb3IgPSBFVU5JUywgZ3JvdXAgPSBFVU5JUykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAjZ2VvbShhZXMoeW1pbiA9IG1lYW5fU0FWSSAtIHNkX1NBVkksIHltYXggPSBtZWFuX1NBVkkgKyBzZF9TQVZJKSwgDQogICAgICAgICAgICAgICAgI3dpZHRoID0gMC4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9udGhseSBTQVZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJTQVZJIiwNCiAgICBjb2xvciA9ICJIYWJpdGF0IChFVU5JUzIpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpnZ3NhdmUoDQogIGhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwgIm1vbnRobHlfc3BlY3Ryb3BoZW5vbG9neV9MYW5kc2F0IiwgIkVVTklTMl9RX1NBVkkuanBlZyIpLA0KICBkcGkgPSAzMDAsIHdpZHRoID0gNywgaGVpZ2h0ID0gMi41KQ0KYGBgDQoNCiMjIyBSDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGFfbW9udGhseV9FVU5JU2FfMiAlPiUgDQogICAgICAgICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8xID09ICJSIiAmICFpcy5uYShFVU5JU2FfMikpICU+JQ0KICAgICAgICAgIyBJZiB3ZSB3YW50IHRvIGhhdmUgbiBwb2ludHMNCiAgICAgICAgICMgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzJfbGFiZWwsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICAgIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgYWVzKHggPSBtb250aCwgeSA9IG1lYW5fTkRWSSwgY29sb3IgPSBFVU5JUywgZ3JvdXAgPSBFVU5JUykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAjZ2VvbShhZXMoeW1pbiA9IG1lYW5fTkRWSSAtIHNkX05EVkksIHltYXggPSBtZWFuX05EVkkgKyBzZF9ORFZJKSwgDQogICAgICAgICAgICAgICAgI3dpZHRoID0gMC4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9udGhseSBORFZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJORFZJIiwNCiAgICBjb2xvciA9ICJIYWJpdGF0IChFVU5JUzIpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpnZ3NhdmUoDQogIGhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwgIm1vbnRobHlfc3BlY3Ryb3BoZW5vbG9neV9MYW5kc2F0IiwgIkVVTklTMl9SX05EVkkuanBlZyIpLA0KICBkcGkgPSAzMDAsIHdpZHRoID0gNywgaGVpZ2h0ID0gMi41KQ0KDQpnZ3Bsb3QoZGF0YV9tb250aGx5X0VVTklTYV8yICU+JSANCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgPT0gIlIiICYgIWlzLm5hKEVVTklTYV8yKSkgJT4lDQogICAgICAgICAjIElmIHdlIHdhbnQgdG8gaGF2ZSBuIHBvaW50cw0KICAgICAgICAgIyBtdXRhdGUoRVVOSVMgPSBwYXN0ZShFVU5JU2FfMl9sYWJlbCwgRVVOSVNhXzJfZGVzY3IsIHNlcCA9ICIgLSAiKSksIA0KICAgICAgICAgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzIsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gbWVhbl9FVkksIGNvbG9yID0gRVVOSVMsIGdyb3VwID0gRVVOSVMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZSgpICsNCiAgI2dlb20oYWVzKHltaW4gPSBtZWFuX0VWSSAtIHNkX0VWSSwgeW1heCA9IG1lYW5fRVZJICsgc2RfRVZJKSwgDQogICAgICAgICAgICAgICAgI3dpZHRoID0gMC4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9udGhseSBFVkkgYnkgSGFiaXRhdCBUeXBlIiwNCiAgICB4ID0gIk1vbnRoIiwNCiAgICB5ID0gIkVWSSIsDQogICAgY29sb3IgPSAiSGFiaXRhdCAoRVVOSVMyKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KZ2dzYXZlKA0KICBoZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsICJtb250aGx5X3NwZWN0cm9waGVub2xvZ3lfTGFuZHNhdCIsICJFVU5JUzJfUl9FVkkuanBlZyIpLA0KICBkcGkgPSAzMDAsIHdpZHRoID0gNywgaGVpZ2h0ID0gMi41KQ0KDQpnZ3Bsb3QoZGF0YV9tb250aGx5X0VVTklTYV8yICU+JSANCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgPT0gIlIiICYgIWlzLm5hKEVVTklTYV8yKSkgJT4lDQogICAgICAgICAjIElmIHdlIHdhbnQgdG8gaGF2ZSBuIHBvaW50cw0KICAgICAgICAgIyBtdXRhdGUoRVVOSVMgPSBwYXN0ZShFVU5JU2FfMl9sYWJlbCwgRVVOSVNhXzJfZGVzY3IsIHNlcCA9ICIgLSAiKSksIA0KICAgICAgICAgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzIsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gbWVhbl9TQVZJLCBjb2xvciA9IEVVTklTLCBncm91cCA9IEVVTklTKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoKSArDQogICNnZW9tKGFlcyh5bWluID0gbWVhbl9TQVZJIC0gc2RfU0FWSSwgeW1heCA9IG1lYW5fU0FWSSArIHNkX1NBVkkpLCANCiAgICAgICAgICAgICAgICAjd2lkdGggPSAwLjIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNb250aGx5IFNBVkkgYnkgSGFiaXRhdCBUeXBlIiwNCiAgICB4ID0gIk1vbnRoIiwNCiAgICB5ID0gIlNBVkkiLA0KICAgIGNvbG9yID0gIkhhYml0YXQgKEVVTklTMikiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmdnc2F2ZSgNCiAgaGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCAibW9udGhseV9zcGVjdHJvcGhlbm9sb2d5X0xhbmRzYXQiLCAiRVVOSVMyX1JfU0FWSS5qcGVnIiksDQogIGRwaSA9IDMwMCwgd2lkdGggPSA3LCBoZWlnaHQgPSAyLjUpDQpgYGANCg0KIyMjIFMNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YV9tb250aGx5X0VVTklTYV8yICU+JSANCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgPT0gIlMiICYgIWlzLm5hKEVVTklTYV8yKSkgJT4lDQogICAgICAgICAjIFJlbW92ZSB0aG9zZSB3aXRoIEVVTklTIGxldmVsIDIgdGhhdCBkb2VzIG5vdCBtYXRjaCBjdXJyZW50IGNsYXNzaWYNCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzIgIT0gIlNhIiAmIEVVTklTYV8yICE9ICJTYiIpICU+JQ0KICAgICAgICAgIyBJZiB3ZSB3YW50IHRvIGhhdmUgbiBwb2ludHMNCiAgICAgICAgICMgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzJfbGFiZWwsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICAgIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgYWVzKHggPSBtb250aCwgeSA9IG1lYW5fTkRWSSwgY29sb3IgPSBFVU5JUywgZ3JvdXAgPSBFVU5JUykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAjZ2VvbShhZXMoeW1pbiA9IG1lYW5fTkRWSSAtIHNkX05EVkksIHltYXggPSBtZWFuX05EVkkgKyBzZF9ORFZJKSwgDQogICAgICAgICAgICAgICAgI3dpZHRoID0gMC4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9udGhseSBORFZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJORFZJIiwNCiAgICBjb2xvciA9ICJIYWJpdGF0IChFVU5JUzIpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpnZ3NhdmUoDQogIGhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwgIm1vbnRobHlfc3BlY3Ryb3BoZW5vbG9neV9MYW5kc2F0IiwgIkVVTklTMl9TX05EVkkuanBlZyIpLA0KICBkcGkgPSAzMDAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gMi41KQ0KDQpnZ3Bsb3QoZGF0YV9tb250aGx5X0VVTklTYV8yICU+JSANCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgPT0gIlMiICYgIWlzLm5hKEVVTklTYV8yKSkgJT4lDQogICAgICAgICAjIFJlbW92ZSB0aG9zZSB3aXRoIEVVTklTIGxldmVsIDIgdGhhdCBkb2VzIG5vdCBtYXRjaCBjdXJyZW50IGNsYXNzaWYNCiAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzIgIT0gIlNhIiAmIEVVTklTYV8yICE9ICJTYiIpICU+JQ0KICAgICAgICAgIyBJZiB3ZSB3YW50IHRvIGhhdmUgbiBwb2ludHMNCiAgICAgICAgICMgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzJfbGFiZWwsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICAgIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgYWVzKHggPSBtb250aCwgeSA9IG1lYW5fRVZJLCBjb2xvciA9IEVVTklTLCBncm91cCA9IEVVTklTKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoKSArDQogICNnZW9tKGFlcyh5bWluID0gbWVhbl9FVkkgLSBzZF9FVkksIHltYXggPSBtZWFuX0VWSSArIHNkX0VWSSksIA0KICAgICAgICAgICAgICAgICN3aWR0aCA9IDAuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1vbnRobHkgRVZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJFVkkiLA0KICAgIGNvbG9yID0gIkhhYml0YXQgKEVVTklTMikiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmdnc2F2ZSgNCiAgaGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCAibW9udGhseV9zcGVjdHJvcGhlbm9sb2d5X0xhbmRzYXQiLCAiRVVOSVMyX1NfRVZJLmpwZWciKSwNCiAgZHBpID0gMzAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDIuNSkNCg0KZ2dwbG90KGRhdGFfbW9udGhseV9FVU5JU2FfMiAlPiUgDQogICAgICAgICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8xID09ICJTIiAmICFpcy5uYShFVU5JU2FfMikpICU+JQ0KICAgICAgICAgIyBSZW1vdmUgdGhvc2Ugd2l0aCBFVU5JUyBsZXZlbCAyIHRoYXQgZG9lcyBub3QgbWF0Y2ggY3VycmVudCBjbGFzc2lmDQogICAgICAgICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8yICE9ICJTYSIgJiBFVU5JU2FfMiAhPSAiU2IiKSAlPiUNCiAgICAgICAgICMgSWYgd2Ugd2FudCB0byBoYXZlIG4gcG9pbnRzDQogICAgICAgICAjIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yX2xhYmVsLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgICBtdXRhdGUoRVVOSVMgPSBwYXN0ZShFVU5JU2FfMiwgRVVOSVNhXzJfZGVzY3IsIHNlcCA9ICIgLSAiKSksIA0KICAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBtZWFuX1NBVkksIGNvbG9yID0gRVVOSVMsIGdyb3VwID0gRVVOSVMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZSgpICsNCiAgI2dlb20oYWVzKHltaW4gPSBtZWFuX1NBVkkgLSBzZF9TQVZJLCB5bWF4ID0gbWVhbl9TQVZJICsgc2RfU0FWSSksIA0KICAgICAgICAgICAgICAgICN3aWR0aCA9IDAuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1vbnRobHkgU0FWSSBieSBIYWJpdGF0IFR5cGUiLA0KICAgIHggPSAiTW9udGgiLA0KICAgIHkgPSAiU0FWSSIsDQogICAgY29sb3IgPSAiSGFiaXRhdCAoRVVOSVMyKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KZ2dzYXZlKA0KICBoZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsICJtb250aGx5X3NwZWN0cm9waGVub2xvZ3lfTGFuZHNhdCIsICJFVU5JUzJfU19TQVZJLmpwZWciKSwNCiAgZHBpID0gMzAwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDIuNSkNCmBgYA0KDQojIyMgVA0KDQpgYGB7cn0NCmdncGxvdChkYXRhX21vbnRobHlfRVVOSVNhXzIgJT4lIA0KICAgICAgICAgZHBseXI6OmZpbHRlcihFVU5JU2FfMSA9PSAiVCIgJiAhaXMubmEoRVVOSVNhXzIpKSAlPiUNCiAgICAgICAgICMgSWYgd2Ugd2FudCB0byBoYXZlIG4gcG9pbnRzDQogICAgICAgICAjIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yX2xhYmVsLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgICBtdXRhdGUoRVVOSVMgPSBwYXN0ZShFVU5JU2FfMiwgRVVOSVNhXzJfZGVzY3IsIHNlcCA9ICIgLSAiKSksIA0KICAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBtZWFuX05EVkksIGNvbG9yID0gRVVOSVMsIGdyb3VwID0gRVVOSVMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZSgpICsNCiAgI2dlb20oYWVzKHltaW4gPSBtZWFuX05EVkkgLSBzZF9ORFZJLCB5bWF4ID0gbWVhbl9ORFZJICsgc2RfTkRWSSksIA0KICAgICAgICAgICAgICAgICN3aWR0aCA9IDAuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1vbnRobHkgTkRWSSBieSBIYWJpdGF0IFR5cGUiLA0KICAgIHggPSAiTW9udGgiLA0KICAgIHkgPSAiTkRWSSIsDQogICAgY29sb3IgPSAiSGFiaXRhdCAoRVVOSVMyKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KZ2dzYXZlKA0KICBoZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsICJtb250aGx5X3NwZWN0cm9waGVub2xvZ3lfTGFuZHNhdCIsICJFVU5JUzJfVF9ORFZJLmpwZWciKSwNCiAgZHBpID0gMzAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkNCg0KZ2dwbG90KGRhdGFfbW9udGhseV9FVU5JU2FfMiAlPiUgDQogICAgICAgICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8xID09ICJUIiAmICFpcy5uYShFVU5JU2FfMikpICU+JQ0KICAgICAgICAgIyBJZiB3ZSB3YW50IHRvIGhhdmUgbiBwb2ludHMNCiAgICAgICAgICMgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzJfbGFiZWwsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICAgIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgYWVzKHggPSBtb250aCwgeSA9IG1lYW5fRVZJLCBjb2xvciA9IEVVTklTLCBncm91cCA9IEVVTklTKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoKSArDQogICNnZW9tKGFlcyh5bWluID0gbWVhbl9FVkkgLSBzZF9FVkksIHltYXggPSBtZWFuX0VWSSArIHNkX0VWSSksIA0KICAgICAgICAgICAgICAgICN3aWR0aCA9IDAuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIk1vbnRobHkgRVZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJFVkkiLA0KICAgIGNvbG9yID0gIkhhYml0YXQgKEVVTklTMikiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmdnc2F2ZSgNCiAgaGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCAibW9udGhseV9zcGVjdHJvcGhlbm9sb2d5X0xhbmRzYXQiLCAiRVVOSVMyX1RfRVZJLmpwZWciKSwNCiAgZHBpID0gMzAwLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkNCg0KZ2dwbG90KGRhdGFfbW9udGhseV9FVU5JU2FfMiAlPiUgDQogICAgICAgICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8xID09ICJUIiAmICFpcy5uYShFVU5JU2FfMikpICU+JQ0KICAgICAgICAgIyBJZiB3ZSB3YW50IHRvIGhhdmUgbiBwb2ludHMNCiAgICAgICAgICMgbXV0YXRlKEVVTklTID0gcGFzdGUoRVVOSVNhXzJfbGFiZWwsIEVVTklTYV8yX2Rlc2NyLCBzZXAgPSAiIC0gIikpLCANCiAgICAgICAgIG11dGF0ZShFVU5JUyA9IHBhc3RlKEVVTklTYV8yLCBFVU5JU2FfMl9kZXNjciwgc2VwID0gIiAtICIpKSwgDQogICAgICAgYWVzKHggPSBtb250aCwgeSA9IG1lYW5fU0FWSSwgY29sb3IgPSBFVU5JUywgZ3JvdXAgPSBFVU5JUykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAjZ2VvbShhZXMoeW1pbiA9IG1lYW5fU0FWSSAtIHNkX1NBVkksIHltYXggPSBtZWFuX1NBVkkgKyBzZF9TQVZJKSwgDQogICAgICAgICAgICAgICAgI3dpZHRoID0gMC4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9udGhseSBTQVZJIGJ5IEhhYml0YXQgVHlwZSIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJTQVZJIiwNCiAgICBjb2xvciA9ICJIYWJpdGF0IChFVU5JUzIpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpnZ3NhdmUoDQogIGhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwgIm1vbnRobHlfc3BlY3Ryb3BoZW5vbG9neV9MYW5kc2F0IiwgIkVVTklTMl9UX1NBVkkuanBlZyIpLA0KICBkcGkgPSAzMDAsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMi41KQ0KYGBgDQoNCiMgQ2FsY3VsYXRlIG90aGVyIHBoZW5vbG9naWNhbCBtZXRyaWNzDQoNCmBgYHtyfQ0KZmluYWxfUlNfZGF0YSA8LSBmaW5hbF9SU19kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgIyB3aXRoIHNsb3BlIG1ldGhvZA0KICAgICMgR3Jvd2luZyBzZWFzb24gZHVyYXRpb24NCiAgICBORFZJX3Nsb3BlX2dzZCA9IE5EVklfZW9zX3Nsb3BlX2RveSAtIE5EVklfc29zX3Nsb3BlX2RveSwNCiAgICBFVklfc2xvcGVfZ3NkID0gTkRWSV9lb3Nfc2xvcGVfZG95IC0gTkRWSV9zb3Nfc2xvcGVfZG95LA0KICAgIFNBVklfc2xvcGVfZ3NkID0gU0FWSV9lb3Nfc2xvcGVfZG95IC0gU0FWSV9zb3Nfc2xvcGVfZG95LA0KICAgICMgRGlmZmVyZW5jZSBpbiB2YWx1ZSBiZXR3ZWVuIHBvcyBhbmQgc29zDQogICAgTkRWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfdmFsdWUgPSBORFZJX3Bvc192YWx1ZSAtIE5EVklfc29zX3Nsb3BlX3ZhbHVlLA0KICAgIEVWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfdmFsdWUgPSBFVklfcG9zX3ZhbHVlIC0gRVZJX3Nvc19zbG9wZV92YWx1ZSwNCiAgICBTQVZJX2RpZmZfcG9zX3Nvc19zbG9wZV92YWx1ZSA9IFNBVklfcG9zX3ZhbHVlIC0gU0FWSV9zb3Nfc2xvcGVfdmFsdWUsDQogICAgIyBEaWZmZXJlbmNlIGluIHZhbHVlIGJldHdlZW4gcG9zIGFuZCBlb3MNCiAgICBORFZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSA9IE5EVklfcG9zX3ZhbHVlIC0gTkRWSV9lb3Nfc2xvcGVfdmFsdWUsDQogICAgRVZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSA9IEVWSV9wb3NfdmFsdWUgLSBFVklfZW9zX3Nsb3BlX3ZhbHVlLA0KICAgIFNBVklfZGlmZl9wb3NfZW9zX3Nsb3BlX3ZhbHVlID0gU0FWSV9wb3NfdmFsdWUgLSBTQVZJX2Vvc19zbG9wZV92YWx1ZSwNCiAgICAjIERpZmZlcmVuY2UgaW4gZG95IGJldHdlZW4gcG9zIGFuZCBzb3MNCiAgICBORFZJX2RpZmZfcG9zX3Nvc19zbG9wZV9kb3kgPSBORFZJX3Bvc19kb3kgLSBORFZJX3Nvc19zbG9wZV9kb3ksDQogICAgRVZJX2RpZmZfcG9zX3Nvc19zbG9wZV9kb3kgPSBFVklfcG9zX2RveSAtIEVWSV9zb3Nfc2xvcGVfZG95LA0KICAgIFNBVklfZGlmZl9wb3Nfc29zX3Nsb3BlX2RveSA9IFNBVklfcG9zX2RveSAtIFNBVklfc29zX3Nsb3BlX2RveSwNCiAgICAjIEFic29sdXRlIGRpZmZlcmVuY2UgaW4gZG95IGJldHdlZW4gcG9zIGFuZCBlb3MNCiAgICBORFZJX2RpZmZfcG9zX2Vvc19zbG9wZV9kb3kgPSBhYnMoTkRWSV9wb3NfZG95IC0gTkRWSV9lb3Nfc2xvcGVfZG95KSwNCiAgICBFVklfZGlmZl9wb3NfZW9zX3Nsb3BlX2RveSA9IGFicyhFVklfcG9zX2RveSAtIEVWSV9lb3Nfc2xvcGVfZG95KSwNCiAgICBTQVZJX2RpZmZfcG9zX2Vvc19zbG9wZV9kb3kgPSBhYnMoU0FWSV9wb3NfZG95IC0gU0FWSV9lb3Nfc2xvcGVfZG95KSwNCiAgICAjIFdpdGggdGhyZXNob2xkIG1ldGhvZA0KICAgICMgR3Jvd2luZyBzZWFzb24gZHVyYXRpb24NCiAgICBORFZJX3RocmVzaG9sZF9nc2QgPSBORFZJX2Vvc190aHJlc2hvbGRfZG95IC0gTkRWSV9zb3NfdGhyZXNob2xkX2RveSwNCiAgICBFVklfdGhyZXNob2xkX2dzZCA9IE5EVklfZW9zX3RocmVzaG9sZF9kb3kgLSBORFZJX3Nvc190aHJlc2hvbGRfZG95LA0KICAgIFNBVklfdGhyZXNob2xkX2dzZCA9IFNBVklfZW9zX3RocmVzaG9sZF9kb3kgLSBTQVZJX3Nvc190aHJlc2hvbGRfZG95LA0KICAgICMgRGlmZmVyZW5jZSBpbiB2YWx1ZSBiZXR3ZWVuIHBvcyBhbmQgc29zDQogICAgTkRWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlID0NCiAgICAgIE5EVklfcG9zX3ZhbHVlIC0gTkRWSV9zb3NfdGhyZXNob2xkX3ZhbHVlLA0KICAgIEVWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlID0gDQogICAgICBFVklfcG9zX3ZhbHVlIC0gRVZJX3Nvc190aHJlc2hvbGRfdmFsdWUsDQogICAgU0FWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlID0gDQogICAgICBTQVZJX3Bvc192YWx1ZSAtIFNBVklfc29zX3RocmVzaG9sZF92YWx1ZSwNCiAgICAjIERpZmZlcmVuY2UgaW4gdmFsdWUgYmV0d2VlbiBwb3MgYW5kIGVvcw0KICAgIE5EVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSA9DQogICAgICBORFZJX3Bvc192YWx1ZSAtIE5EVklfZW9zX3RocmVzaG9sZF92YWx1ZSwNCiAgICBFVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSA9IA0KICAgICAgRVZJX3Bvc192YWx1ZSAtIEVWSV9lb3NfdGhyZXNob2xkX3ZhbHVlLA0KICAgIFNBVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSA9IA0KICAgICAgU0FWSV9wb3NfdmFsdWUgLSBTQVZJX2Vvc190aHJlc2hvbGRfdmFsdWUsDQogICAgIyBEaWZmZXJlbmNlIGluIGRveSBiZXR3ZWVuIHBvcyBhbmQgc29zDQogICAgTkRWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX2RveSA9IE5EVklfcG9zX2RveSAtIE5EVklfc29zX3RocmVzaG9sZF9kb3ksDQogICAgRVZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfZG95ID0gRVZJX3Bvc19kb3kgLSBFVklfc29zX3RocmVzaG9sZF9kb3ksDQogICAgU0FWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX2RveSA9IFNBVklfcG9zX2RveSAtIFNBVklfc29zX3RocmVzaG9sZF9kb3ksDQogICAgIyBBYnNvbHV0ZSBkaWZmZXJlbmNlIGluIGRveSBiZXR3ZWVuIHBvcyBhbmQgZW9zDQogICAgTkRWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX2RveSA9IA0KICAgICAgYWJzKE5EVklfcG9zX2RveSAtIE5EVklfZW9zX3RocmVzaG9sZF9kb3kpLA0KICAgIEVWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX2RveSA9IA0KICAgICAgYWJzKEVWSV9wb3NfZG95IC0gRVZJX2Vvc190aHJlc2hvbGRfZG95KSwNCiAgICBTQVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfZG95ID0gDQogICAgICBhYnMoU0FWSV9wb3NfZG95IC0gU0FWSV9lb3NfdGhyZXNob2xkX2RveSksDQogICAgIyBXaXRoIG1vbnRocyBtZXRob2QNCiAgICAjIERpZmZlcmVuY2UgaW4gdmFsdWUgYmV0d2VlbiBwb3MgYW5kIG1hcmNoDQogICAgTkRWSV9kaWZmX3Bvc19tYXJjaF92YWx1ZSA9IE5EVklfcG9zX3ZhbHVlIC0gTkRWSV9hdmdfdmFsdWVfMDMsDQogICAgRVZJX2RpZmZfcG9zX21hcmNoX3ZhbHVlID0gRVZJX3Bvc192YWx1ZSAtIEVWSV9hdmdfdmFsdWVfMDMsDQogICAgU0FWSV9kaWZmX3Bvc19tYXJjaF92YWx1ZSA9IFNBVklfcG9zX3ZhbHVlIC0gU0FWSV9hdmdfdmFsdWVfMDMsDQogICAgIyBEaWZmZXJlbmNlIGluIHZhbHVlIGJldHdlZW4gcG9zIGFuZCBvY3QNCiAgICBORFZJX2RpZmZfcG9zX29jdF92YWx1ZSA9IE5EVklfcG9zX3ZhbHVlIC0gTkRWSV9hdmdfdmFsdWVfMTAsDQogICAgRVZJX2RpZmZfcG9zX29jdF92YWx1ZSA9IEVWSV9wb3NfdmFsdWUgLSBFVklfYXZnX3ZhbHVlXzEwLA0KICAgIFNBVklfZGlmZl9wb3Nfb2N0X3ZhbHVlID0gU0FWSV9wb3NfdmFsdWUgLSBTQVZJX2F2Z192YWx1ZV8xMCwNCiAgICAjIERpZmZlcmVuY2UgaW4gZG95IGJldHdlZW4gcG9zIGFuZCBtYXJjaA0KICAgIE5EVklfZGlmZl9wb3NfbWFyY2hfZG95ID0gTkRWSV9wb3NfZG95IC0gNzUsIA0KICAgIEVWSV9kaWZmX3Bvc19tYXJjaF9kb3kgPSBFVklfcG9zX2RveSAtIDc1LA0KICAgIFNBVklfZGlmZl9wb3NfbWFyY2hfZG95ID0gU0FWSV9wb3NfZG95IC0gNzUsDQogICAgIyBEaWZmZXJlbmNlIGluIGRveSBiZXR3ZWVuIHBvcyBhbmQgb2N0DQogICAgTkRWSV9kaWZmX3Bvc19vY3RfZG95ID0gYWJzKE5EVklfcG9zX2RveSAtIDI4NSksDQogICAgRVZJX2RpZmZfcG9zX29jdF9kb3kgPSBhYnMoRVZJX3Bvc19kb3kgLSAyODUpLA0KICAgIFNBVklfZGlmZl9wb3Nfb2N0X2RveSA9IGFicyhTQVZJX3Bvc19kb3kgLSAyODUpDQogICkNCmBgYA0KDQojIyBDaGVja3MNCg0KIyMjIFNsb3BlIG1ldGhvZA0KDQpgYGB7cn0NCiMgR3Jvd2luZyBzZWFzb24gZHVyYXRpb24gc2hvdWxkIGJlIHBvc2l0aXZlDQpucm93KGZpbmFsX1JTX2RhdGEgJT4lIA0KICAgICAgIGRwbHlyOjpmaWx0ZXIoTkRWSV9zbG9wZV9nc2QgPD0gMCB8IEVWSV9zbG9wZV9nc2QgPD0gMCB8IA0KICAgICAgICAgICAgICAgICAgICAgICBTQVZJX3Nsb3BlX2dzZCA8PSAwKSkNCiMgRGlmZmVyZW5jZSBpbiB2YWx1ZSBiZXR3ZWVuIHBvcyBhbmQgc29zIHNob3VsZCBiZSBwb3NpdGl2ZQ0KbnJvdyhmaW5hbF9SU19kYXRhICU+JQ0KICAgICAgIGRwbHlyOjpmaWx0ZXIoTkRWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfdmFsdWUgPD0gMCkpDQpucm93KGZpbmFsX1JTX2RhdGEgJT4lDQogICAgICAgZHBseXI6OmZpbHRlcihFVklfZGlmZl9wb3Nfc29zX3Nsb3BlX3ZhbHVlIDw9IDApKQ0KbnJvdyhmaW5hbF9SU19kYXRhICU+JQ0KICAgICAgIGRwbHlyOjpmaWx0ZXIoU0FWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfdmFsdWUgPD0gMCkpDQojIERpZmZlcmVuY2UgaW4gdmFsdWUgYmV0d2VlbiBwb3MgYW5kIGVvcyBzaG91bGQgYmUgcG9zaXRpdmUNCm5yb3coZmluYWxfUlNfZGF0YSAlPiUNCiAgICAgICBkcGx5cjo6ZmlsdGVyKE5EVklfZGlmZl9wb3NfZW9zX3Nsb3BlX3ZhbHVlIDw9IDApKQ0KbnJvdyhmaW5hbF9SU19kYXRhICU+JQ0KICAgICAgIGRwbHlyOjpmaWx0ZXIoRVZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSA8PSAwKSkNCm5yb3coZmluYWxfUlNfZGF0YSAlPiUNCiAgICAgICBkcGx5cjo6ZmlsdGVyKFNBVklfZGlmZl9wb3NfZW9zX3Nsb3BlX3ZhbHVlIDw9IDApKQ0KIyBEaWZmZXJlbmNlIGluIGRveSBiZXR3ZWVuIHBvcyBhbmQgc29zIHNob3VsZCBiZSBwb3NpdGl2ZQ0KbnJvdyhmaW5hbF9SU19kYXRhICU+JQ0KICAgICAgIGRwbHlyOjpmaWx0ZXIoTkRWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IDw9IDAgfA0KICAgICAgICAgICAgICAgICAgICAgICBFVklfZGlmZl9wb3Nfc29zX3Nsb3BlX2RveSA8PSAwIHwNCiAgICAgICAgICAgICAgICAgICAgICAgU0FWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IDw9IDApKQ0KIyBEaWZmZXJlbmNlIGluIGRveSBiZXR3ZWVuIGVvcyBhbmQgcG9zIHNob3VsZCBiZSBwb3NpdGl2ZQ0KbnJvdyhmaW5hbF9SU19kYXRhICU+JQ0KICAgICAgIGRwbHlyOjpmaWx0ZXIoTkRWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IDw9IDAgfA0KICAgICAgICAgICAgICAgICAgICAgICBFVklfZGlmZl9wb3NfZW9zX3Nsb3BlX2RveSA8PSAwIHwNCiAgICAgICAgICAgICAgICAgICAgICAgU0FWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IDw9IDApKQ0KYGBgDQoNCiMjIyBUaHJlc2hvbGQgbWV0aG9kDQoNCmBgYHtyfQ0KIyBHcm93aW5nIHNlYXNvbiBkdXJhdGlvbiBzaG91bGQgYmUgcG9zaXRpdmUNCm5yb3coZmluYWxfUlNfZGF0YSAlPiUgDQogICAgICAgZHBseXI6OmZpbHRlcihORFZJX3RocmVzaG9sZF9nc2QgPD0gMCB8IEVWSV90aHJlc2hvbGRfZ3NkIDw9IDAgfCANCiAgICAgICAgICAgICAgICAgICAgICAgU0FWSV90aHJlc2hvbGRfZ3NkIDw9IDApKQ0KIyBEaWZmZXJlbmNlIGluIHZhbHVlIGJldHdlZW4gcG9zIGFuZCBzb3Mgc2hvdWxkIGJlIHBvc2l0aXZlDQpucm93KGZpbmFsX1JTX2RhdGEgJT4lDQogICAgICAgZHBseXI6OmZpbHRlcihORFZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfdmFsdWUgPD0gMCkpDQpucm93KGZpbmFsX1JTX2RhdGEgJT4lDQogICAgICAgZHBseXI6OmZpbHRlcihFVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF92YWx1ZSA8PSAwKSkNCm5yb3coZmluYWxfUlNfZGF0YSAlPiUNCiAgICAgICBkcGx5cjo6ZmlsdGVyKFNBVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF92YWx1ZSA8PSAwKSkNCiMgRGlmZmVyZW5jZSBpbiB2YWx1ZSBiZXR3ZWVuIHBvcyBhbmQgZW9zIHNob3VsZCBiZSBwb3NpdGl2ZQ0KbnJvdyhmaW5hbF9SU19kYXRhICU+JQ0KICAgICAgIGRwbHlyOjpmaWx0ZXIoTkRWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX3ZhbHVlIDw9IDApKQ0KbnJvdyhmaW5hbF9SU19kYXRhICU+JQ0KICAgICAgIGRwbHlyOjpmaWx0ZXIoRVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfdmFsdWUgPD0gMCkpDQpucm93KGZpbmFsX1JTX2RhdGEgJT4lDQogICAgICAgZHBseXI6OmZpbHRlcihTQVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfdmFsdWUgPD0gMCkpDQojIERpZmZlcmVuY2UgaW4gZG95IGJldHdlZW4gcG9zIGFuZCBzb3Mgc2hvdWxkIGJlIHBvc2l0aXZlDQpucm93KGZpbmFsX1JTX2RhdGEgJT4lDQogICAgICAgZHBseXI6OmZpbHRlcihORFZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfZG95IDw9IDAgfA0KICAgICAgICAgICAgICAgICAgICAgICBFVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF9kb3kgPD0gMCB8DQogICAgICAgICAgICAgICAgICAgICAgIFNBVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF9kb3kgPD0gMCkpDQojIERpZmZlcmVuY2UgaW4gZG95IGJldHdlZW4gZW9zIGFuZCBwb3Mgc2hvdWxkIGJlIHBvc2l0aXZlDQpucm93KGZpbmFsX1JTX2RhdGEgJT4lDQogICAgICAgZHBseXI6OmZpbHRlcihORFZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfZG95IDw9IDAgfA0KICAgICAgICAgICAgICAgICAgICAgICBFVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF9kb3kgPD0gMCB8DQogICAgICAgICAgICAgICAgICAgICAgIFNBVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF9kb3kgPD0gMCkpDQpgYGANCg0KIyBNYXliZSBUQkQ6IFBlYWsgZGV0ZWN0aW9uDQoNClByZWxpbWluYXJ5IGNvZGUgaW4gOV9TMl9iYW5kc19hbGxfZXh0cmFjdF9kYXRhLiBTbyBmYXIgbm90IHdvcmtpbmcsIHNlZSBpZiB3ZSBmaW5hbGx5IGRvIHRoaXMgb3Igbm90Lg0KDQojIEFkZCBzb21lIGNvbHVtbnMgbmVlZGVkDQoNCmBgYHtyfQ0KZmluYWxfUlNfZGF0YSA8LSBmaW5hbF9SU19kYXRhICU+JQ0KICBsZWZ0X2pvaW4oDQogICAgZGF0YV9SU19MYW5kc2F0X2JhbmRzX2luZGljZXMgJT4lDQogICAgICBkaXN0aW5jdChQbG90T2JzZXJ2YXRpb25JRCwgeWVhciwgYmlvZ2VvLCB1bml0LCBMY3RubXRoKQ0KICAgICkNCmBgYA0KDQojIEFkZCBjYW5vcHkgaGVpZ2h0IGRhdGENCg0KUmVhZCB0aGUgZGF0YToNCg0KYGBge3J9DQpkYXRhX1JTX0NIIDwtIHJlYWRfY3N2KA0KICAiQzovRGF0YS9NT1RJVkFURS9NT1RJVkFURV9SU19kYXRhL0Nhbm9weV9IZWlnaHRfMW0vRXVyb3BlX3BvaW50c19DYW5vcHlIZWlnaHRfMW0uY3N2IikNCmRiX0V1cm9wYSA8LSByZWFkX2NzdigNCiAgaGVyZSgiLi4iLCAiREJfZmlyc3RfY2hlY2siLCAiZGF0YSIsICJjbGVhbiIsImRiX0V1cm9wYV8yMDI1MDEwNy5jc3YiKQ0KICApDQpgYGANCg0KYGBge3J9DQpkYXRhX1JTX0NIX0lEIDwtIGRiX0V1cm9wYSAlPiUNCiAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBvYnNfdW5pcXVlX2lkKSAlPiUNCiAgcmlnaHRfam9pbihkYXRhX1JTX0NIICU+JQ0KICAgICAgICAgICAgICAjIFJlbmFtZSB0byBiZSBhYmxlIHRvIGpvaW4gb24gdGhpcyBjb2x1bW4NCiAgICAgICAgICAgICAgcmVuYW1lKG9ic191bmlxdWVfaWQgPSBvYnNfdW5pcXVlKSkgJT4lDQogIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgY2Fub3B5X2hlaWdodCkNCmBgYA0KDQpKb2luOg0KDQpgYGB7cn0NCmZpbmFsX1JTX2RhdGEgPC0gZmluYWxfUlNfZGF0YSAlPiUNCiAgbGVmdF9qb2luKGRhdGFfUlNfQ0hfSUQgJT4lDQogICAgICAgICAgICAgIG11dGF0ZShQbG90T2JzZXJ2YXRpb25JRCA9IGZhY3RvcihQbG90T2JzZXJ2YXRpb25JRCkpKQ0KYGBgDQoNCiMgU2F2ZSB0byBjbGVhbiBkYXRhDQoNCmBgYHtyfQ0Kd3JpdGVfdHN2KGZpbmFsX1JTX2RhdGEsDQogICAgICAgICAgaGVyZSgiZGF0YSIsICJjbGVhbiIsImZpbmFsX1JTX2RhdGFfYmFuZHNfTGFuZHNhdF9hbGxfMjAyNTA5MjUuY3N2IikpDQpgYGANCg0KIyBTZXNzaW9uIGluZm8NCg0KYGBge3J9DQpzZXNzaW9uSW5mbygpDQpgYGANCg==